├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── README_zh_cn.md ├── SharpRedis.sln ├── alipay.png ├── examples ├── Example_NET30 │ ├── Example_NET30.csproj │ ├── Program.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── Example_NET46 │ ├── App.config │ ├── Example_NET46.csproj │ ├── Program.cs │ └── Properties │ │ └── AssemblyInfo.cs ├── Example_NET8 │ ├── Example_NET8.csproj │ ├── LocalCache.cs │ ├── Program.cs │ └── ProgramLocalCache.cs └── Example_NET9 │ ├── Example_NET9.csproj │ └── Program.cs ├── src ├── SharpRedis.Autofac │ ├── RegistrationExtensions.cs │ └── SharpRedis.Autofac.csproj ├── SharpRedis.DependencyInjection │ ├── RegistrationExtensions.cs │ └── SharpRedis.DependencyInjection.csproj ├── SharpRedis.DependencyInjectionKeyedService │ ├── RegistrationExtensions.cs │ └── SharpRedis.DependencyInjectionKeyedService.csproj ├── SharpRedis.Unity │ ├── RegistrationExtensions.cs │ └── SharpRedis.Unity.csproj └── SharpRedis │ ├── Commands │ ├── BitmapCommands.cs │ ├── ConnectionCommands.cs │ ├── GeospatialCommands.cs │ ├── HashCommands.cs │ ├── HyperLogLogCommands.cs │ ├── KeyCommands.cs │ ├── ListCommands.cs │ ├── PubSubCommands.cs │ ├── ScriptCommands.cs │ ├── ServerCommands.cs │ ├── SetCommands.cs │ ├── SortedSetCommands.cs │ ├── StreamCommands.cs │ ├── StringCommands.cs │ └── TransactionCommands.cs │ ├── Exceptions │ ├── RedisConnectionException.cs │ ├── RedisException.cs │ └── RedisInitializationException.cs │ ├── Extensions │ ├── ClientSideCachingExtensions.cs │ ├── ConnectionExtensions.cs │ ├── ConvertExtensions.cs │ ├── DataPacketExtensions.cs │ ├── Extend.cs │ ├── RedisExtensions.cs │ ├── SharpConsole.cs │ ├── StringExtensions.cs │ ├── net30 │ │ ├── Action.cs │ │ ├── Redis.cs │ │ └── RedisSwitchDatabase.cs │ ├── net30_35_40_45 │ │ └── Array.cs │ └── net35_30 │ │ ├── CancellationToken.cs │ │ ├── CancellationTokenSource.cs │ │ ├── ConcurrentDictionary.cs │ │ ├── ConcurrentQueue.cs │ │ ├── ConcurrentStack.cs │ │ └── StructuralEqualityComparer.cs │ ├── Models │ ├── BitFieldArg.cs │ ├── ChannelMode.cs │ ├── ClientSideCacheKey.cs │ ├── CommandPacket.cs │ ├── ConnectionOptions.cs │ ├── Delegates.cs │ ├── Enums.cs │ ├── OnReceiveModel.cs │ ├── Standard │ │ ├── IBitFieldArg.cs │ │ ├── IBitFieldArgEncoding.cs │ │ ├── IBitFieldArgIncrement.cs │ │ ├── IBitFieldArgOffset.cs │ │ ├── IBitFieldArgOverflow.cs │ │ └── IBitFieldArgValue.cs │ └── StreamTrimOptions.cs │ ├── Network │ ├── DefaultConnection.cs │ ├── Pool │ │ ├── DefaultPool.cs │ │ └── MasterSlavePool.cs │ ├── Standard │ │ ├── BaseConnection.cs │ │ ├── BaseConnectionPool.cs │ │ ├── IConnection.cs │ │ └── IConnectionPool.cs │ └── SubConnection.cs │ ├── Provider │ ├── Calls │ │ ├── ClientSideCachingCall.cs │ │ ├── DefaultCall.cs │ │ ├── MasterSlaveCall.cs │ │ ├── PipeliningCall.cs │ │ ├── SwitchDatabaseCall.cs │ │ └── TransactionCall.cs │ ├── Redis.cs │ ├── RedisPipelining.cs │ ├── RedisSwitchDatabase.cs │ ├── RedisTransaction.cs │ ├── Standard │ │ ├── BaseCall.cs │ │ ├── BaseRedis.cs │ │ ├── BaseType.cs │ │ ├── ClientSideCachingStandard.cs │ │ └── FeatureRedis.cs │ └── Types │ │ ├── Bitmap │ │ ├── RedisBitmap.cs │ │ └── RedisBitmapAsync.cs │ │ ├── Geospatial │ │ ├── RedisGeospatialIndices.cs │ │ ├── RedisGeospatialIndicesAsync.cs │ │ └── RedisGeospatialIndicesSpan.cs │ │ ├── Hash │ │ ├── RedisHash.cs │ │ ├── RedisHashAsync.cs │ │ └── RedisHashSpan.cs │ │ ├── HyperLogLog │ │ ├── RedisHyperLogLog.cs │ │ ├── RedisHyperLogLogAsync.cs │ │ └── RedisHyperLogLogSpan.cs │ │ ├── Key │ │ ├── RedisKey.cs │ │ └── RedisKeyAsync.cs │ │ ├── List │ │ ├── RedisList.cs │ │ ├── RedisListAsync.cs │ │ └── RedisListSpan.cs │ │ ├── PubSub │ │ ├── RedisPubSub.cs │ │ ├── RedisPubSubAsync.cs │ │ └── RedisPubSubSpan.cs │ │ ├── RedisConnection.cs │ │ ├── Script │ │ ├── RedisScript.cs │ │ └── RedisScriptAsync.cs │ │ ├── Server │ │ ├── RedisServer.cs │ │ └── RedisServerAsync.cs │ │ ├── Set │ │ ├── RedisSet.cs │ │ ├── RedisSetAsync.cs │ │ └── RedisSetSpan.cs │ │ ├── SortedSet │ │ ├── RedisSortedSet.cs │ │ ├── RedisSortedSetAsync.cs │ │ └── RedisSortedSetSpan.cs │ │ ├── Stream │ │ ├── RedisStream.cs │ │ ├── RedisStreamAsync.cs │ │ └── RedisStreamSpan.cs │ │ └── String │ │ ├── RedisString.cs │ │ ├── RedisStringAsync.cs │ │ └── RedisStringSpan.cs │ ├── SharpRedis.csproj │ └── Values │ ├── BooleanValue.cs │ ├── CoordinateValue.cs │ ├── FunctionInfoValue.cs │ ├── FunctionStatsValue.cs │ ├── GeoRadiusValue.cs │ ├── LcsValue.cs │ ├── MemberScoreValue.cs │ ├── NumberValue.cs │ ├── ScanValue.cs │ ├── ScoreRankValue.cs │ ├── StreamValue.cs │ ├── TransactionValue.cs │ ├── XAutoClaimValue.cs │ ├── XInfoConsumersValue.cs │ ├── XInfoGroupsValue.cs │ ├── XInfoStreamFullValue.cs │ └── XInfoStreamValue.cs ├── test └── NET8_Test │ ├── NET8_Test.csproj │ ├── RedisProvider.cs │ └── Types │ ├── GeoTest.cs │ ├── HashTest.cs │ ├── ListTest.cs │ ├── ScriptTest.cs │ ├── SetTest.cs │ ├── SortedSetTest.cs │ └── StringTest.cs └── wechat.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 SharpRedis Project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /SharpRedis.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 17 3 | VisualStudioVersion = 17.10.35027.167 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{954381ED-C4AD-45EB-AD53-C764813E0605}" 6 | EndProject 7 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{AF6960AD-895B-4D10-8444-F0550606B52F}" 8 | EndProject 9 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpRedis", "src\SharpRedis\SharpRedis.csproj", "{AA255161-C3F4-498B-8104-A92E908398B6}" 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example_NET30", "examples\Example_NET30\Example_NET30.csproj", "{B8FBAFFC-7324-405D-A63E-6FC6C2CB583B}" 12 | EndProject 13 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example_NET46", "examples\Example_NET46\Example_NET46.csproj", "{7A5B557D-2A2D-4A11-9CAE-9AFF8097EA7C}" 14 | EndProject 15 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Example_NET8", "examples\Example_NET8\Example_NET8.csproj", "{56FD24A1-C7D9-4078-81F0-58175D91CFA9}" 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{25AA3EFE-8ACA-4534-A87E-A7ECA043C89F}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NET8_Test", "test\NET8_Test\NET8_Test.csproj", "{3D38F0BC-A5C7-49FE-8141-2C44B6E811AA}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpRedis.Autofac", "src\SharpRedis.Autofac\SharpRedis.Autofac.csproj", "{0B8182F7-0231-4260-8B94-416D64F55504}" 22 | EndProject 23 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpRedis.Unity", "src\SharpRedis.Unity\SharpRedis.Unity.csproj", "{1700E6DB-20DE-4BB8-B196-4C9E9495AD27}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpRedis.DependencyInjection", "src\SharpRedis.DependencyInjection\SharpRedis.DependencyInjection.csproj", "{C93AAC1A-BF01-4351-A73E-6D83D9E3EE7D}" 26 | EndProject 27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpRedis.DependencyInjectionKeyedService", "src\SharpRedis.DependencyInjectionKeyedService\SharpRedis.DependencyInjectionKeyedService.csproj", "{BF90AB6F-46F4-49F4-AF9B-EEF710F765DD}" 28 | EndProject 29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example_NET9", "examples\Example_NET9\Example_NET9.csproj", "{4E0B764B-88A1-4348-BA7C-CF1D1693D7E5}" 30 | EndProject 31 | Global 32 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 33 | Debug|Any CPU = Debug|Any CPU 34 | Release|Any CPU = Release|Any CPU 35 | EndGlobalSection 36 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 37 | {AA255161-C3F4-498B-8104-A92E908398B6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {AA255161-C3F4-498B-8104-A92E908398B6}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {AA255161-C3F4-498B-8104-A92E908398B6}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {AA255161-C3F4-498B-8104-A92E908398B6}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {B8FBAFFC-7324-405D-A63E-6FC6C2CB583B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {B8FBAFFC-7324-405D-A63E-6FC6C2CB583B}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {B8FBAFFC-7324-405D-A63E-6FC6C2CB583B}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {B8FBAFFC-7324-405D-A63E-6FC6C2CB583B}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {7A5B557D-2A2D-4A11-9CAE-9AFF8097EA7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 46 | {7A5B557D-2A2D-4A11-9CAE-9AFF8097EA7C}.Debug|Any CPU.Build.0 = Debug|Any CPU 47 | {7A5B557D-2A2D-4A11-9CAE-9AFF8097EA7C}.Release|Any CPU.ActiveCfg = Release|Any CPU 48 | {7A5B557D-2A2D-4A11-9CAE-9AFF8097EA7C}.Release|Any CPU.Build.0 = Release|Any CPU 49 | {56FD24A1-C7D9-4078-81F0-58175D91CFA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 50 | {56FD24A1-C7D9-4078-81F0-58175D91CFA9}.Debug|Any CPU.Build.0 = Debug|Any CPU 51 | {56FD24A1-C7D9-4078-81F0-58175D91CFA9}.Release|Any CPU.ActiveCfg = Release|Any CPU 52 | {56FD24A1-C7D9-4078-81F0-58175D91CFA9}.Release|Any CPU.Build.0 = Release|Any CPU 53 | {3D38F0BC-A5C7-49FE-8141-2C44B6E811AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 54 | {3D38F0BC-A5C7-49FE-8141-2C44B6E811AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 55 | {3D38F0BC-A5C7-49FE-8141-2C44B6E811AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 56 | {3D38F0BC-A5C7-49FE-8141-2C44B6E811AA}.Release|Any CPU.Build.0 = Release|Any CPU 57 | {0B8182F7-0231-4260-8B94-416D64F55504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 58 | {0B8182F7-0231-4260-8B94-416D64F55504}.Debug|Any CPU.Build.0 = Debug|Any CPU 59 | {0B8182F7-0231-4260-8B94-416D64F55504}.Release|Any CPU.ActiveCfg = Release|Any CPU 60 | {0B8182F7-0231-4260-8B94-416D64F55504}.Release|Any CPU.Build.0 = Release|Any CPU 61 | {1700E6DB-20DE-4BB8-B196-4C9E9495AD27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 62 | {1700E6DB-20DE-4BB8-B196-4C9E9495AD27}.Debug|Any CPU.Build.0 = Debug|Any CPU 63 | {1700E6DB-20DE-4BB8-B196-4C9E9495AD27}.Release|Any CPU.ActiveCfg = Release|Any CPU 64 | {1700E6DB-20DE-4BB8-B196-4C9E9495AD27}.Release|Any CPU.Build.0 = Release|Any CPU 65 | {C93AAC1A-BF01-4351-A73E-6D83D9E3EE7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 66 | {C93AAC1A-BF01-4351-A73E-6D83D9E3EE7D}.Debug|Any CPU.Build.0 = Debug|Any CPU 67 | {C93AAC1A-BF01-4351-A73E-6D83D9E3EE7D}.Release|Any CPU.ActiveCfg = Release|Any CPU 68 | {C93AAC1A-BF01-4351-A73E-6D83D9E3EE7D}.Release|Any CPU.Build.0 = Release|Any CPU 69 | {BF90AB6F-46F4-49F4-AF9B-EEF710F765DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 70 | {BF90AB6F-46F4-49F4-AF9B-EEF710F765DD}.Debug|Any CPU.Build.0 = Debug|Any CPU 71 | {BF90AB6F-46F4-49F4-AF9B-EEF710F765DD}.Release|Any CPU.ActiveCfg = Release|Any CPU 72 | {BF90AB6F-46F4-49F4-AF9B-EEF710F765DD}.Release|Any CPU.Build.0 = Release|Any CPU 73 | {4E0B764B-88A1-4348-BA7C-CF1D1693D7E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 74 | {4E0B764B-88A1-4348-BA7C-CF1D1693D7E5}.Debug|Any CPU.Build.0 = Debug|Any CPU 75 | {4E0B764B-88A1-4348-BA7C-CF1D1693D7E5}.Release|Any CPU.ActiveCfg = Release|Any CPU 76 | {4E0B764B-88A1-4348-BA7C-CF1D1693D7E5}.Release|Any CPU.Build.0 = Release|Any CPU 77 | EndGlobalSection 78 | GlobalSection(SolutionProperties) = preSolution 79 | HideSolutionNode = FALSE 80 | EndGlobalSection 81 | GlobalSection(NestedProjects) = preSolution 82 | {AA255161-C3F4-498B-8104-A92E908398B6} = {954381ED-C4AD-45EB-AD53-C764813E0605} 83 | {B8FBAFFC-7324-405D-A63E-6FC6C2CB583B} = {AF6960AD-895B-4D10-8444-F0550606B52F} 84 | {7A5B557D-2A2D-4A11-9CAE-9AFF8097EA7C} = {AF6960AD-895B-4D10-8444-F0550606B52F} 85 | {56FD24A1-C7D9-4078-81F0-58175D91CFA9} = {AF6960AD-895B-4D10-8444-F0550606B52F} 86 | {3D38F0BC-A5C7-49FE-8141-2C44B6E811AA} = {25AA3EFE-8ACA-4534-A87E-A7ECA043C89F} 87 | {0B8182F7-0231-4260-8B94-416D64F55504} = {954381ED-C4AD-45EB-AD53-C764813E0605} 88 | {1700E6DB-20DE-4BB8-B196-4C9E9495AD27} = {954381ED-C4AD-45EB-AD53-C764813E0605} 89 | {C93AAC1A-BF01-4351-A73E-6D83D9E3EE7D} = {954381ED-C4AD-45EB-AD53-C764813E0605} 90 | {BF90AB6F-46F4-49F4-AF9B-EEF710F765DD} = {954381ED-C4AD-45EB-AD53-C764813E0605} 91 | {4E0B764B-88A1-4348-BA7C-CF1D1693D7E5} = {AF6960AD-895B-4D10-8444-F0550606B52F} 92 | EndGlobalSection 93 | GlobalSection(ExtensibilityGlobals) = postSolution 94 | SolutionGuid = {47F37E65-68EA-49EA-B6BC-D45DE1E856FD} 95 | EndGlobalSection 96 | EndGlobal 97 | -------------------------------------------------------------------------------- /alipay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q7164518/SharpRedis/77755066436b6e8cfb08d469a1256e1a59f998f3/alipay.png -------------------------------------------------------------------------------- /examples/Example_NET30/Example_NET30.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {B8FBAFFC-7324-405D-A63E-6FC6C2CB583B} 8 | Exe 9 | Example_NET30 10 | Example_NET30 11 | v3.0 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | true 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {58ed8253-a396-4934-8b60-9330a5a46d46} 47 | SharpRedis 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /examples/Example_NET30/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using SharpRedis; 6 | 7 | namespace Example_NET30 8 | { 9 | internal class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | var redis = Redis.UseStandalone(f => 14 | { 15 | f.Password = "123456"; 16 | f.MaxPoolSize = 300; 17 | }); 18 | redis.Server.FlushAll(); 19 | redis.String.Set("k1", "k1"); 20 | redis.String.Set("num", "1"); 21 | redis.Hash.HSet("h", "f", "嘿嘿"); 22 | redis.List.RPush("list", new string[] { "123", "456" }); 23 | 24 | 25 | for (int i = 0; i < 300; i++) 26 | { 27 | new Thread(() => 28 | { 29 | int indexx = i; 30 | int iii = indexx % 2; 31 | while (true) 32 | { 33 | var k1 = redis.String.Get("k1"); 34 | if (k1 != "k1") Console.WriteLine("k1错误"); 35 | 36 | redis.String.Incr("num"); 37 | 38 | var hf = redis.Hash.HGet("h", "f"); 39 | if (hf != "嘿嘿") Console.WriteLine("hash错误"); 40 | 41 | var list = redis.List.LIndex("list", iii); 42 | if (iii == 0 && list != "123") Console.WriteLine("list错误"); 43 | if (iii == 1 && list != "456") Console.WriteLine("list错误"); 44 | } 45 | }).Start(); 46 | } 47 | Console.WriteLine("启动成功, NET3.0"); 48 | Console.ReadLine(); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/Example_NET30/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("Example_NET30")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Example_NET30")] 13 | [assembly: AssemblyCopyright("Copyright © 2024")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("b8fbaffc-7324-405d-a63e-6fc6c2cb583b")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/Example_NET46/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /examples/Example_NET46/Example_NET46.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {7A5B557D-2A2D-4A11-9CAE-9AFF8097EA7C} 8 | Exe 9 | Example_NET46 10 | Example_NET46 11 | v4.6 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | {58ed8253-a396-4934-8b60-9330a5a46d46} 55 | SharpRedis 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /examples/Example_NET46/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using SharpRedis; 8 | 9 | namespace Example_NET46 10 | { 11 | internal class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Console.ReadLine(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/Example_NET46/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // 有关程序集的一般信息由以下 6 | // 控制。更改这些特性值可修改 7 | // 与程序集关联的信息。 8 | [assembly: AssemblyTitle("Example_NET46")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Example_NET46")] 13 | [assembly: AssemblyCopyright("Copyright © 2024")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // 将 ComVisible 设置为 false 会使此程序集中的类型 18 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 19 | //请将此类型的 ComVisible 特性设置为 true。 20 | [assembly: ComVisible(false)] 21 | 22 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID 23 | [assembly: Guid("7a5b557d-2a2d-4a11-9cae-9aff8097ea7c")] 24 | 25 | // 程序集的版本信息由下列四个值组成: 26 | // 27 | // 主版本 28 | // 次版本 29 | // 生成号 30 | // 修订号 31 | // 32 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 33 | //通过使用 "*",如下所示: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /examples/Example_NET8/Example_NET8.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | True 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /examples/Example_NET8/LocalCache.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Caching.Memory; 2 | using SharpRedis; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace Example_NET8; 6 | 7 | sealed public class LocalCache : ClientSideCachingStandard 8 | { 9 | private readonly MemoryCache _cache; 10 | 11 | public LocalCache() 12 | { 13 | this._cache = new MemoryCache(new MemoryCacheOptions { }); 14 | } 15 | 16 | public override ClientSideCachingMode Mode => ClientSideCachingMode.Broadcasting; 17 | 18 | public override string[]? KeyPatterns => ["localcache_test*"]; 19 | 20 | public override string[]? WithoutKeyPatterns => ["nocache*"]; 21 | 22 | public override string[]? KeyPrefixes => ["localcache_test:"]; 23 | 24 | protected override bool Clear() 25 | { 26 | this._cache.Clear(); 27 | return true; 28 | } 29 | 30 | protected override bool Delete(in ClientSideCacheKey key) 31 | { 32 | this._cache.Remove(key); 33 | return true; 34 | } 35 | 36 | protected override bool Set(in ClientSideCacheKey key, object value) 37 | { 38 | this._cache.Set(key, value); 39 | return true; 40 | } 41 | 42 | protected override bool TryGet(in ClientSideCacheKey key, [NotNullWhen(true)] out object? value) 43 | { 44 | return this._cache.TryGetValue(key, out value); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/Example_NET8/Program.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis; 2 | var redis = Redis.UseStandalone(f => 3 | { 4 | f.Password = "123456"; 5 | f.MaxPoolSize = 300; 6 | }); 7 | 8 | //redis.PubSub.Subscribe("test", static (a, b) => 9 | //{ 10 | // Console.WriteLine($"Channel: {a}, Data: {b}"); 11 | // return Task.CompletedTask; 12 | //}); 13 | 14 | //var chang = await redis.String.GetAsync("chang"); 15 | //; 16 | await redis.Server.FlushAllAsync(); 17 | await redis.String.SetAsync("k1", "k1"); 18 | await redis.String.SetAsync("num", "1"); 19 | await redis.Hash.HSetAsync("h", "f", "嘿嘿"); 20 | await redis.List.RPushAsync("list", ["123", "456"]); 21 | 22 | 23 | for (int i = 0; i < 300; i++) 24 | { 25 | new Thread(async () => 26 | { 27 | int indexx = i; 28 | int iii = indexx % 2; 29 | while (true) 30 | { 31 | var k1 = await redis.String.GetAsync("k1"); 32 | if (k1 != "k1") Console.WriteLine("k1错误"); 33 | 34 | await redis.String.IncrAsync("num"); 35 | 36 | var hf = await redis.Hash.HGetAsync("h", "f"); 37 | if (hf != "嘿嘿") Console.WriteLine("hash错误"); 38 | 39 | var list = await redis.List.LIndexAsync("list", iii); 40 | if (iii == 0 && list != "123") Console.WriteLine("list错误"); 41 | if (iii == 1 && list != "456") Console.WriteLine("list错误"); 42 | } 43 | }).Start(); 44 | } 45 | Console.WriteLine("启动成功.NET8"); 46 | Console.ReadLine(); 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | ////using Autofac; 73 | ////using Example_NET8; 74 | ////using SharpRedis; 75 | ////using SharpRedis.Autofac; 76 | 77 | ////#region Autofac 78 | ////{ 79 | //// var builder = new ContainerBuilder(); 80 | //// builder.AddSharpRedisStandalone(option => 81 | //// { 82 | //// option.Password = "123456"; 83 | //// }); 84 | 85 | //// builder.AddSharpRedisStandalone(option => 86 | //// { 87 | //// option.Password = "123456"; 88 | //// }, "localCache"); 89 | 90 | //// var container = builder.Build(); 91 | 92 | //// { 93 | //// var redis = container.Resolve(); 94 | //// var ok = await redis.String.SetAsync("key1", "Hello SharpRedis.Autofac"); 95 | //// var get = await redis.String.GetAsync("key1"); 96 | 97 | //// var redisString = container.Resolve(); 98 | //// get = await redisString.GetAsync("key1"); 99 | //// } 100 | 101 | //// { 102 | //// var redis = container.ResolveNamed("localCache"); 103 | //// var ok = await redis.String.SetAsync("localcache_test:key1", "Hello SharpRedis.Autofac, LocalCache"); 104 | //// var get = await redis.String.GetAsync("localcache_test:key1"); 105 | //// get = await redis.String.GetAsync("localcache_test:key1"); 106 | 107 | //// var redisString = container.ResolveNamed("localCache"); 108 | //// get = await redisString.GetAsync("localcache_test:key1"); 109 | //// get = await redisString.GetAsync("none:key"); 110 | //// } 111 | ////} 112 | ////#endregion 113 | 114 | ////#region Unity 115 | 116 | ////#endregion 117 | 118 | ////Console.ReadLine(); -------------------------------------------------------------------------------- /examples/Example_NET8/ProgramLocalCache.cs: -------------------------------------------------------------------------------- 1 | //using Autofac; 2 | //using Example_NET8; 3 | //using SharpRedis; 4 | //using SharpRedis.Autofac; 5 | 6 | //#region Autofac 7 | //{ 8 | // var builder = new ContainerBuilder(); 9 | // builder.AddSharpRedisStandalone(option => 10 | // { 11 | // option.Password = "123456"; 12 | // }); 13 | 14 | // builder.AddSharpRedisStandalone(option => 15 | // { 16 | // option.Password = "123456"; 17 | // }, "localCache"); 18 | 19 | // var container = builder.Build(); 20 | 21 | // { 22 | // using var fdsf = new CancellationTokenSource(); 23 | // fdsf.CancelAfter(TimeSpan.FromSeconds(5)); 24 | // var redis = container.Resolve(); 25 | // var ok = await redis.String.SetAsync("key1", "Hello SharpRedis.Autofac", fdsf.Token); 26 | // var get = await redis.String.GetAsync("key1"); 27 | 28 | // var redisString = container.Resolve(); 29 | // get = await redisString.GetAsync("key1"); 30 | // } 31 | 32 | // { 33 | // var redis = container.ResolveNamed("localCache"); 34 | // var ok = await redis.String.SetAsync("localcache_test:key1", "Hello SharpRedis.Autofac, LocalCache"); 35 | // var get = await redis.String.GetAsync("localcache_test:key1"); 36 | // get = await redis.String.GetAsync("localcache_test:key1"); 37 | 38 | // var redisString = container.ResolveNamed("localCache"); 39 | // get = await redisString.GetAsync("localcache_test:key1"); 40 | // get = await redisString.GetAsync("none:key"); 41 | // while (true) 42 | // { 43 | // //Console.WriteLine(await redis.String.GetAsync("localcache_test:key1")); 44 | // Console.WriteLine(await redis.String.MGetAsync(["localcache_test:key1","ffds"])); 45 | // Console.ReadLine(); 46 | // } 47 | // } 48 | //} 49 | //#endregion 50 | 51 | //#region Unity 52 | 53 | //#endregion 54 | 55 | //Console.ReadLine(); -------------------------------------------------------------------------------- /examples/Example_NET9/Example_NET9.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | True 9 | True 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/Example_NET9/Program.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis; 2 | var redis = Redis.UseStandalone(f => 3 | { 4 | f.Password = "123456"; 5 | f.MaxPoolSize = 300; 6 | }); 7 | 8 | await redis.Server.FlushAllAsync(); 9 | await redis.String.SetAsync("k1", "k1"); 10 | await redis.String.SetAsync("num", "1"); 11 | await redis.Hash.HSetAsync("h", "f", "嘿嘿"); 12 | await redis.List.RPushAsync("list", ["123", "456"]); 13 | 14 | 15 | for (int i = 0; i < 300; i++) 16 | { 17 | new Thread(async () => 18 | { 19 | int indexx = i; 20 | int iii = indexx % 2; 21 | while (true) 22 | { 23 | var k1 = await redis.String.GetAsync("k1"); 24 | if (k1 != "k1") Console.WriteLine("k1错误"); 25 | 26 | await redis.String.IncrAsync("num"); 27 | 28 | var hf = await redis.Hash.HGetAsync("h", "f"); 29 | if (hf != "嘿嘿") Console.WriteLine("hash错误"); 30 | 31 | var list = await redis.List.LIndexAsync("list", iii); 32 | if (iii == 0 && list != "123") Console.WriteLine("list错误"); 33 | if (iii == 1 && list != "456") Console.WriteLine("list错误"); 34 | } 35 | }).Start(); 36 | } 37 | Console.WriteLine("启动成功.NET9"); 38 | Console.ReadLine(); -------------------------------------------------------------------------------- /src/SharpRedis.Autofac/SharpRedis.Autofac.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0 5 | SharpRedis.Autofac 6 | SharpRedis.Autofac 7 | 0.0.7.0 8 | Kwong 9 | A pure asynchronous driver of high performance high throughput Redis driver for C# 10 | SharpRedis.Autofac;SharpRedis;sharpredis;Redis;redis;Redics;RedisClient;RedisHelper;csredis;csharpredis;redisclients;C#Redis;Kwong;kwong;q7164518 11 | false 12 | true 13 | True 14 | Kwong 15 | True 16 | https://github.com/q7164518/SharpRedis 17 | https://github.com/q7164518/SharpRedis 18 | git 19 | 20 | 21 | 22 | enable 23 | 24 | 25 | enable 26 | 27 | 28 | enable 29 | 30 | 31 | enable 32 | 33 | 34 | enable 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/SharpRedis.DependencyInjection/RegistrationExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | 4 | namespace SharpRedis.DependencyInjection 5 | { 6 | /// 7 | /// SharpRedis Microsoft.Extensions.DependencyInjection Extensions 8 | /// 9 | public static class RegistrationExtensions 10 | { 11 | /// 12 | /// Connect singleton Redis and register with Microsoft.Extensions.DependencyInjection ServiceCollection 13 | /// 连接单例Redis, 并注册到Microsoft.Extensions.DependencyInjection ServiceCollection 14 | /// 15 | /// IServiceCollection 16 | /// connection string 17 | /// 连接字符串 18 | /// 19 | /// optionsAction 20 | /// 21 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 22 | public static IServiceCollection AddSharpRedisStandalone(this IServiceCollection services, string connectionString, Action? optionsAction = null) 23 | #else 24 | public static IServiceCollection AddSharpRedisStandalone(this IServiceCollection services, string connectionString, Action optionsAction = null) 25 | #endif 26 | { 27 | var redis = Redis.UseStandalone(connectionString, optionsAction); 28 | 29 | return services.AddSingleton(redis) 30 | .RegisterTypes(); 31 | } 32 | 33 | /// 34 | /// Connect singleton Redis and register with Microsoft.Extensions.DependencyInjection ServiceCollection 35 | /// 连接单例Redis, 并注册到Microsoft.Extensions.DependencyInjection ServiceCollection 36 | /// 37 | /// IServiceCollection 38 | /// optionsAction 39 | /// 40 | public static IServiceCollection AddSharpRedisStandalone(this IServiceCollection services, Action optionsAction) 41 | { 42 | return services.AddSharpRedisStandalone(string.Empty, optionsAction); 43 | } 44 | 45 | /// 46 | /// Connect singleton Redis and register with Microsoft.Extensions.DependencyInjection ServiceCollection 47 | /// 连接单例Redis, 并注册到Microsoft.Extensions.DependencyInjection ServiceCollection 48 | /// 49 | /// The local cache implements generics 50 | /// 本地缓存实现泛型 51 | /// 52 | /// IServiceCollection 53 | /// connection string 54 | /// 连接字符串 55 | /// 56 | /// optionsAction 57 | /// 58 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 59 | public static IServiceCollection AddSharpRedisStandalone(this IServiceCollection services, string connectionString, Action? optionsAction = null) 60 | #else 61 | public static IServiceCollection AddSharpRedisStandalone(this IServiceCollection services, string connectionString, Action optionsAction = null) 62 | #endif 63 | where TCache : ClientSideCachingStandard 64 | { 65 | return services.AddSingleton() 66 | .AddSingleton(c => 67 | { 68 | var cache = c.GetService(); 69 | var redis = Redis.UseStandalone(connectionString, options => 70 | { 71 | optionsAction?.Invoke(options); 72 | options.SetClientSideCaching(cache); 73 | }); 74 | return redis; 75 | }) 76 | .RegisterTypes(); 77 | } 78 | 79 | /// 80 | /// Connect singleton Redis and register with Microsoft.Extensions.DependencyInjection ServiceCollection 81 | /// 连接单例Redis, 并注册到Microsoft.Extensions.DependencyInjection ServiceCollection 82 | /// 83 | /// The local cache implements generics 84 | /// 本地缓存实现泛型 85 | /// 86 | /// IServiceCollection 87 | /// optionsAction 88 | /// 89 | public static IServiceCollection AddSharpRedisStandalone(this IServiceCollection services, Action optionsAction) 90 | where TCache : ClientSideCachingStandard 91 | { 92 | return services.AddSharpRedisStandalone(string.Empty, optionsAction); 93 | } 94 | 95 | private static IServiceCollection RegisterTypes(this IServiceCollection services) 96 | { 97 | return services.AddSingleton(c => c.GetService().String) 98 | .AddSingleton(c => c.GetService().Hash) 99 | .AddSingleton(c => c.GetService().List) 100 | .AddSingleton(c => c.GetService().Set) 101 | .AddSingleton(c => c.GetService().SortedSet) 102 | .AddSingleton(c => c.GetService().Bitmap) 103 | .AddSingleton(c => c.GetService().HyperLogLog) 104 | .AddSingleton(c => c.GetService().Geospatial) 105 | .AddSingleton(c => c.GetService().Script) 106 | .AddSingleton(c => c.GetService().Connection) 107 | .AddSingleton(c => c.GetService().Key) 108 | .AddSingleton(c => c.GetService().Server) 109 | .AddSingleton(c => c.GetService().PubSub) 110 | .AddSingleton(c => c.GetService().Stream); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/SharpRedis.DependencyInjection/SharpRedis.DependencyInjection.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0 5 | SharpRedis.DependencyInjection 6 | SharpRedis.DependencyInjection 7 | 0.0.7.0 8 | Kwong 9 | A pure asynchronous driver of high performance high throughput Redis driver for C# 10 | SharpRedis.DependencyInjection;SharpRedis;sharpredis;Redis;redis;Redics;RedisClient;RedisHelper;csredis;csharpredis;redisclients;C#Redis;Kwong;kwong;q7164518 11 | false 12 | true 13 | True 14 | Kwong 15 | True 16 | https://github.com/q7164518/SharpRedis 17 | https://github.com/q7164518/SharpRedis 18 | git 19 | 20 | 21 | 22 | enable 23 | 24 | 25 | enable 26 | 27 | 28 | enable 29 | 30 | 31 | enable 32 | 33 | 34 | enable 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/SharpRedis.DependencyInjectionKeyedService/SharpRedis.DependencyInjectionKeyedService.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0 5 | SharpRedis.DependencyInjectionKeyedService 6 | SharpRedis.DependencyInjectionKeyedService 7 | 0.0.7.0 8 | Kwong 9 | A pure asynchronous driver of high performance high throughput Redis driver for C# 10 | SharpRedis.DependencyInjectionKeyedService;SharpRedis;sharpredis;Redis;redis;Redics;RedisClient;RedisHelper;csredis;csharpredis;redisclients;C#Redis;Kwong;kwong;q7164518 11 | false 12 | true 13 | True 14 | Kwong 15 | True 16 | https://github.com/q7164518/SharpRedis 17 | https://github.com/q7164518/SharpRedis 18 | git 19 | 20 | 21 | 22 | enable 23 | 24 | 25 | enable 26 | 27 | 28 | enable 29 | 30 | 31 | enable 32 | 33 | 34 | enable 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/SharpRedis.Unity/SharpRedis.Unity.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0 5 | SharpRedis.Unity 6 | SharpRedis.Unity 7 | 0.0.7.0 8 | Kwong 9 | A pure asynchronous driver of high performance high throughput Redis driver for C# 10 | SharpRedis.Unity;SharpRedis;sharpredis;Redis;redis;Redics;RedisClient;RedisHelper;csredis;csharpredis;redisclients;C#Redis;Kwong;kwong;q7164518 11 | false 12 | true 13 | True 14 | Kwong 15 | True 16 | https://github.com/q7164518/SharpRedis 17 | https://github.com/q7164518/SharpRedis 18 | git 19 | 20 | 21 | 22 | enable 23 | 24 | 25 | enable 26 | 27 | 28 | enable 29 | 30 | 31 | enable 32 | 33 | 34 | enable 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/SharpRedis/Commands/BitmapCommands.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis.Models; 2 | 3 | namespace SharpRedis.Commands 4 | { 5 | internal static class BitmapCommands 6 | { 7 | static internal CommandPacket SetBit(string key, uint offset, bool value) 8 | { 9 | return new CommandPacket("SETBIT", CommandMode.Write) 10 | .WriteKey(key) 11 | .WriteArg(offset) 12 | .WriteValue(value, "1") 13 | .WriteValue(!value, "0"); 14 | } 15 | 16 | internal static CommandPacket GetBit(string key, uint offset) 17 | { 18 | return new CommandPacket("GETBIT", CommandMode.Read | CommandMode.WithClientSideCache) 19 | .WriteKey(key) 20 | .WriteArg(offset); 21 | } 22 | 23 | internal static CommandPacket BitPos(string key, bool bit, long? start = null, long? end = null, ByteBit bb = ByteBit.None) 24 | { 25 | if (!start.HasValue && end.HasValue) throw new RedisException("Setting only the end location is not allowed"); 26 | return new CommandPacket("BITPOS", CommandMode.Read | CommandMode.WithClientSideCache) 27 | .WriteKey(key) 28 | .WriteValue(bit, "1") 29 | .WriteValue(!bit, "0") 30 | .WriteArg(start ?? 0) 31 | .WriteArg(end ?? -1) 32 | .WriteArg(bb == ByteBit.Bit, "BIT") 33 | .WriteArg(bb == ByteBit.Byte, "BYTE"); 34 | } 35 | 36 | internal static CommandPacket BitOp(BitOperation bitOperation, string destkey, params string[] keys) 37 | { 38 | if (keys is null || keys.Length == 0) throw new RedisException("Ensure that at least one key participates in the calculation"); 39 | if (bitOperation != BitOperation.Not && keys.Length <= 1) throw new RedisException("A non-NOT bit operation requires at least two keys"); 40 | return new CommandPacket("BITOP", CommandMode.Write) 41 | .WriteArg(bitOperation is BitOperation.And, "AND") 42 | .WriteArg(bitOperation is BitOperation.Or, "OR") 43 | .WriteArg(bitOperation is BitOperation.Xor, "XOR") 44 | .WriteArg(bitOperation is BitOperation.Not, "NOT") 45 | .WriteKey(destkey) 46 | .WriteKeys(keys); 47 | } 48 | 49 | internal static CommandPacket BitCount(string key, long? start = null, long? end = null, ByteBit bb = ByteBit.None) 50 | { 51 | if (!start.HasValue && end.HasValue) throw new RedisException("Setting only the end location is not allowed"); 52 | return new CommandPacket("BITCOUNT", CommandMode.Read | CommandMode.WithClientSideCache) 53 | .WriteKey(key) 54 | .WriteArg(start ?? 0) 55 | .WriteArg(end ?? -1) 56 | .WriteArg(bb == ByteBit.Bit, "BIT") 57 | .WriteArg(bb == ByteBit.Byte, "BYTE"); 58 | } 59 | 60 | internal static CommandPacket BitField(string key, params IBitFieldArg[] args) 61 | { 62 | if (args is null || args.Length == 0) throw new RedisException("The BITFIELD command parameter cannot be empty"); 63 | var command = new CommandPacket("BITFIELD", CommandMode.Write).WriteKey(key); 64 | for (uint i = 0; i < args.Length; i++) 65 | { 66 | if (args[i] is null) throw new RedisException("The BITFIELD command is null"); 67 | command.WriteValues(args[i].Convert()); 68 | } 69 | return command; 70 | } 71 | 72 | internal static CommandPacket BitField_Ro(string key, params IBitFieldArg[] args) 73 | { 74 | if (args is null || args.Length == 0) throw new RedisException("The BITFIELD command parameter cannot be empty"); 75 | var command = new CommandPacket("BITFIELD_RO", CommandMode.Read | CommandMode.WithClientSideCache).WriteKey(key); 76 | for (uint i = 0; i < args.Length; i++) 77 | { 78 | if (args[i] is null) throw new RedisException("The BITFIELD command is null"); 79 | if (args[i].ArgType != BitFieldArgType.Get) throw new RedisException("The BITFIELD_RO command supports only the GET arg"); 80 | command.WriteValues(args[i].Convert()); 81 | } 82 | return command; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/SharpRedis/Commands/ConnectionCommands.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis.Models; 2 | 3 | namespace SharpRedis.Commands 4 | { 5 | internal static class ConnectionCommands 6 | { 7 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 8 | internal static CommandPacket Auth(string? user, string? password) 9 | #else 10 | internal static CommandPacket Auth(string user, string password) 11 | #endif 12 | { 13 | return new CommandPacket("AUTH", CommandMode.Connection) 14 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 15 | .WriteArg(!string.IsNullOrEmpty(user), user!) 16 | .WriteArg(!string.IsNullOrEmpty(password), password!); 17 | #else 18 | .WriteArg(!string.IsNullOrEmpty(user), user) 19 | .WriteArg(!string.IsNullOrEmpty(password), password); 20 | #endif 21 | } 22 | 23 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 24 | internal static CommandPacket Hello(RespVersion protover, string? user, string? password, string? clientname) 25 | #else 26 | internal static CommandPacket Hello(RespVersion protover, string user, string password, string clientname) 27 | #endif 28 | { 29 | var command = new CommandPacket("HELLO", CommandMode.Connection) 30 | .WriteArg((int)protover) 31 | .WriteArg(!string.IsNullOrEmpty(user) || !string.IsNullOrEmpty(password), "AUTH") 32 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 33 | .WriteValue(!string.IsNullOrEmpty(user), user!) 34 | #else 35 | .WriteValue(!string.IsNullOrEmpty(user), user) 36 | #endif 37 | .WriteValue(string.IsNullOrEmpty(user), "default") 38 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 39 | .WriteValue(!string.IsNullOrEmpty(password), password!); 40 | #else 41 | .WriteValue(!string.IsNullOrEmpty(password), password); 42 | #endif 43 | if (!string.IsNullOrEmpty(clientname)) 44 | { 45 | command.WriteArg("SETNAME") 46 | .WriteValue(clientname); 47 | } 48 | return command; 49 | } 50 | 51 | internal static CommandPacket Echo(TValue message) where TValue : class 52 | { 53 | return new CommandPacket("ECHO", CommandMode.Connection) 54 | .WriteValue(message); 55 | } 56 | 57 | internal static CommandPacket Select(uint index) 58 | { 59 | return new CommandPacket("SELECT", CommandMode.Connection) 60 | .WriteArg(index); 61 | } 62 | 63 | internal static CommandPacket ClientSetName(string clientname) 64 | { 65 | return new CommandPacket("CLIENT", CommandMode.Connection) 66 | .WriteArg("SETNAME") 67 | .WriteValue(clientname); 68 | } 69 | 70 | internal static CommandPacket ClientGetName() 71 | => new CommandPacket("CLIENT", CommandMode.Connection).WriteArg("GETNAME"); 72 | 73 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 74 | internal static CommandPacket Ping(string? message) 75 | #else 76 | internal static CommandPacket Ping(string message) 77 | #endif 78 | { 79 | return new CommandPacket("PING", CommandMode.Connection | CommandMode.WithoutActiveTime) 80 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 81 | .WriteValue(!string.IsNullOrEmpty(message), message!); 82 | #else 83 | .WriteValue(!string.IsNullOrEmpty(message), message); 84 | #endif 85 | } 86 | 87 | internal static CommandPacket ClientId() => new CommandPacket("CLIENT", CommandMode.Connection).WriteArg("ID"); 88 | 89 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 90 | internal static CommandPacket ClientTracking(bool onOff, long clientId, string[]? prefixes = null, bool bcast = false, bool optin = false, bool optout = false, bool noloop = false) 91 | #else 92 | internal static CommandPacket ClientTracking(bool onOff, long clientId, string[] prefixes = null, bool bcast = false, bool optin = false, bool optout = false, bool noloop = false) 93 | #endif 94 | { 95 | var command = new CommandPacket("CLIENT", CommandMode.Connection) 96 | .WriteArg("TRACKING") 97 | .WriteArg(onOff, "ON") 98 | .WriteArg(!onOff, "OFF") 99 | .WriteArg("REDIRECT", clientId); 100 | 101 | if (prefixes?.Length > 0) 102 | { 103 | for (uint i = 0; i < prefixes.Length; i++) 104 | { 105 | command.WriteArg("PREFIX", prefixes[i]); 106 | } 107 | } 108 | command.WriteArg(bcast, "BCAST") 109 | .WriteArg(optin, "OPTIN") 110 | .WriteArg(optout, "OPTOUT") 111 | .WriteArg(noloop, "NOLOOP"); 112 | return command; 113 | } 114 | 115 | internal static CommandPacket ClientCaching(bool yn) 116 | { 117 | return new CommandPacket("CLIENT", CommandMode.Connection) 118 | .WriteArg("CACHING") 119 | .WriteValue(yn, "YES") 120 | .WriteValue(!yn, "NO"); 121 | } 122 | 123 | internal static CommandPacket ClientGetRedir() 124 | => new CommandPacket("CLIENT", CommandMode.Connection).WriteArg("GETREDIR"); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/SharpRedis/Commands/HyperLogLogCommands.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis.Models; 2 | 3 | namespace SharpRedis.Commands 4 | { 5 | internal static class HyperLogLogCommands 6 | { 7 | internal static CommandPacket PFAdd(string key, params TElement[] elements) where TElement : class 8 | { 9 | return new CommandPacket("PFADD", CommandMode.Write) 10 | .WriteKey(key) 11 | .WriteValues(elements); 12 | } 13 | 14 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 15 | internal static CommandPacket PFCount(string key, string[]? keys) 16 | #else 17 | internal static CommandPacket PFCount(string key, string[] keys) 18 | #endif 19 | { 20 | return new CommandPacket("PFCOUNT", CommandMode.Read | CommandMode.WithClientSideCache) 21 | .WriteKey(key) 22 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 23 | .WriteKeys(keys?.Length > 0, keys!); 24 | #else 25 | .WriteKeys(keys?.Length > 0, keys); 26 | #endif 27 | } 28 | 29 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 30 | internal static CommandPacket PFMerge(string destkey, string? sourceKey, string[]? sourcekeys) 31 | #else 32 | internal static CommandPacket PFMerge(string destkey, string sourceKey, string[] sourcekeys) 33 | #endif 34 | { 35 | return new CommandPacket("PFMERGE", CommandMode.Write) 36 | .WriteKey(destkey) 37 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 38 | .WriteKey(!string.IsNullOrEmpty(sourceKey), sourceKey!) 39 | .WriteKeys(sourcekeys?.Length > 0, sourcekeys!); 40 | #else 41 | .WriteKey(!string.IsNullOrEmpty(sourceKey), sourceKey) 42 | .WriteKeys(sourcekeys?.Length > 0, sourcekeys); 43 | #endif 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/SharpRedis/Commands/PubSubCommands.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER 2 | #pragma warning disable IDE0090 3 | #endif 4 | using SharpRedis.Models; 5 | 6 | namespace SharpRedis.Commands 7 | { 8 | internal static class PubSubCommands 9 | { 10 | internal static CommandPacket Publish(string channel, TMessage message) where TMessage : class 11 | { 12 | return new CommandPacket("PUBLISH", CommandMode.Pub) 13 | .WriteValue(channel) 14 | .WriteValue(message); 15 | } 16 | 17 | internal static CommandPacket SPublish(string shardChannel, TMessage message) where TMessage : class 18 | { 19 | return new CommandPacket("SPUBLISH", CommandMode.Pub) 20 | .WriteValue(shardChannel) 21 | .WriteValue(message); 22 | } 23 | 24 | internal static CommandPacket Subscribe() => new CommandPacket("SUBSCRIBE", CommandMode.Sub | CommandMode.WithoutResult); 25 | 26 | internal static CommandPacket SSubscribe() => new CommandPacket("SSUBSCRIBE", CommandMode.Sub | CommandMode.WithoutResult); 27 | 28 | internal static CommandPacket PSubscribe() => new CommandPacket("PSUBSCRIBE", CommandMode.Sub | CommandMode.WithoutResult); 29 | 30 | internal static CommandPacket UnSubscribe() => new CommandPacket("UNSUBSCRIBE", CommandMode.UnSub | CommandMode.WithoutResult); 31 | 32 | internal static CommandPacket PUnSubscribe() => new CommandPacket("PUNSUBSCRIBE", CommandMode.UnSub | CommandMode.WithoutResult); 33 | 34 | internal static CommandPacket SUnSubscribe() => new CommandPacket("SUNSUBSCRIBE", CommandMode.UnSub | CommandMode.WithoutResult); 35 | 36 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 37 | internal static CommandPacket PubSubChannels(string? pattern) 38 | #else 39 | internal static CommandPacket PubSubChannels(string pattern) 40 | #endif 41 | { 42 | return new CommandPacket("PUBSUB", CommandMode.Pub) 43 | .WriteArg("CHANNELS") 44 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 45 | .WriteValue(!string.IsNullOrEmpty(pattern), pattern!); 46 | #else 47 | .WriteValue(!string.IsNullOrEmpty(pattern), pattern); 48 | #endif 49 | } 50 | 51 | internal static CommandPacket PubSubNumPAt() => new CommandPacket("PUBSUB", CommandMode.Pub).WriteArg("NUMPAT"); 52 | 53 | internal static CommandPacket PubSubNumSub(string[] channels) 54 | { 55 | return new CommandPacket("PUBSUB", CommandMode.Pub) 56 | .WriteArg("NUMSUB") 57 | .WriteValues(channels); 58 | } 59 | 60 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 61 | internal static CommandPacket PubSubShardChannels(string? pattern) 62 | #else 63 | internal static CommandPacket PubSubShardChannels(string pattern) 64 | #endif 65 | { 66 | return new CommandPacket("PUBSUB", CommandMode.Pub) 67 | .WriteArg("SHARDCHANNELS") 68 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 69 | .WriteValue(!string.IsNullOrEmpty(pattern), pattern!); 70 | #else 71 | .WriteValue(!string.IsNullOrEmpty(pattern), pattern); 72 | #endif 73 | } 74 | 75 | internal static CommandPacket PubSubShardNumSub(string[] shardchannels) 76 | { 77 | return new CommandPacket("PUBSUB", CommandMode.Pub) 78 | .WriteArg("SHARDNUMSUB") 79 | .WriteValues(shardchannels); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/SharpRedis/Commands/ServerCommands.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER 2 | #pragma warning disable IDE0090 3 | #endif 4 | using SharpRedis.Models; 5 | 6 | namespace SharpRedis.Commands 7 | { 8 | internal static class ServerCommands 9 | { 10 | internal static CommandPacket FlushAll(FlushingMode mode) 11 | { 12 | return new CommandPacket("FLUSHALL", CommandMode.Server | CommandMode.Write) 13 | .WriteArg(mode == FlushingMode.Sync, "SYNC") 14 | .WriteArg(mode == FlushingMode.Async, "ASYNC"); 15 | } 16 | 17 | internal static CommandPacket FlushDb(FlushingMode mode) 18 | { 19 | return new CommandPacket("FLUSHDB", CommandMode.Server | CommandMode.Write) 20 | .WriteArg(mode == FlushingMode.Sync, "SYNC") 21 | .WriteArg(mode == FlushingMode.Async, "ASYNC"); 22 | } 23 | 24 | internal static CommandPacket Save() 25 | => new CommandPacket("SAVE", CommandMode.Server); 26 | 27 | internal static CommandPacket BgSave(bool schedule) 28 | => new CommandPacket("BGSAVE", CommandMode.Server).WriteArg(schedule, "SCHEDULE"); 29 | 30 | internal static CommandPacket Info(params string[] sections) 31 | { 32 | return new CommandPacket("INFO", CommandMode.Server) 33 | .WriteArgs(sections); 34 | } 35 | 36 | internal static CommandPacket DbSize() 37 | { 38 | return new CommandPacket("DBSIZE", CommandMode.Server | CommandMode.Read); 39 | } 40 | 41 | internal static CommandPacket LastSave() 42 | { 43 | return new CommandPacket("LASTSAVE", CommandMode.Server); 44 | } 45 | 46 | internal static CommandPacket BgRewriteAof() 47 | { 48 | return new CommandPacket("BGREWRITEAOF", CommandMode.Server); 49 | } 50 | 51 | internal static CommandPacket CommandCount() 52 | { 53 | return new CommandPacket("COMMAND", CommandMode.Server).WriteArg("COUNT"); 54 | } 55 | 56 | internal static CommandPacket CommandGetKeys(params string[] command) 57 | { 58 | return new CommandPacket("COMMAND", CommandMode.Server) 59 | .WriteArg("GETKEYS") 60 | .WriteArgs(command); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/SharpRedis/Commands/SetCommands.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis.Models; 2 | 3 | namespace SharpRedis.Commands 4 | { 5 | internal static class SetCommands 6 | { 7 | internal static CommandPacket SAdd(string key, params TMember[] members) where TMember : class 8 | { 9 | return new CommandPacket("SADD", CommandMode.Write) 10 | .WriteKey(key) 11 | .WriteValues(members); 12 | } 13 | 14 | internal static CommandPacket SCard(string key) 15 | { 16 | return new CommandPacket("SCARD", CommandMode.Read | CommandMode.WithClientSideCache) 17 | .WriteKey(key); 18 | } 19 | 20 | internal static CommandPacket SDiff(string[] keys) 21 | { 22 | return new CommandPacket("SDIFF", CommandMode.Read | CommandMode.WithClientSideCache) 23 | .WriteKeys(keys); 24 | } 25 | 26 | internal static CommandPacket SDiffStore(string destinationKey, string[] keys) 27 | { 28 | return new CommandPacket("SDIFFSTORE", CommandMode.Write) 29 | .WriteKey(destinationKey) 30 | .WriteKeys(keys); 31 | } 32 | 33 | internal static CommandPacket SInter(string[] keys) 34 | { 35 | return new CommandPacket("SINTER", CommandMode.Read | CommandMode.WithClientSideCache) 36 | .WriteKeys(keys); 37 | } 38 | 39 | internal static CommandPacket SInterCard(string[] keys, ulong limit) 40 | { 41 | return new CommandPacket("SINTERCARD", CommandMode.Read | CommandMode.WithClientSideCache) 42 | .WriteArg(keys.Length) 43 | .WriteKeys(keys) 44 | .WriteArg(limit > 0, "LIMIT", limit); 45 | } 46 | 47 | internal static CommandPacket SInterStore(string destinationKey, string[] keys) 48 | { 49 | return new CommandPacket("SINTERSTORE", CommandMode.Write) 50 | .WriteKey(destinationKey) 51 | .WriteKeys(keys); 52 | } 53 | 54 | internal static CommandPacket SIsMember(string key, TMember member) where TMember : class 55 | { 56 | return new CommandPacket("SISMEMBER", CommandMode.Read | CommandMode.WithClientSideCache) 57 | .WriteKey(key) 58 | .WriteValue(member); 59 | } 60 | 61 | internal static CommandPacket SMembers(string key) 62 | { 63 | return new CommandPacket("SMEMBERS", CommandMode.Read | CommandMode.WithClientSideCache) 64 | .WriteKey(key); 65 | } 66 | 67 | internal static CommandPacket SMIsMember(string key, params TMember[] members) where TMember : class 68 | { 69 | return new CommandPacket("SMISMEMBER", CommandMode.Read | CommandMode.WithClientSideCache) 70 | .WriteKey(key) 71 | .WriteValues(members); 72 | } 73 | 74 | internal static CommandPacket SMove(string source, string destination, TMember member) where TMember : class 75 | { 76 | return new CommandPacket("SMOVE", CommandMode.Write) 77 | .WriteKey(source) 78 | .WriteKey(destination) 79 | .WriteValue(member); 80 | } 81 | 82 | internal static CommandPacket SPop(string key, ulong count) 83 | { 84 | if (count <= 0) throw new RedisException("Count cannot be 0"); 85 | return new CommandPacket("SPOP", CommandMode.Write) 86 | .WriteKey(key) 87 | .WriteArg(count > 1, count); 88 | } 89 | 90 | internal static CommandPacket SRandMember(string key, long count) 91 | { 92 | return new CommandPacket("SRANDMEMBER", CommandMode.Read) 93 | .WriteKey(key) 94 | .WriteArg(count != 0, count); 95 | } 96 | 97 | internal static CommandPacket SRem(string key, params TMember[] members) where TMember : class 98 | { 99 | return new CommandPacket("SREM", CommandMode.Write) 100 | .WriteKey(key) 101 | .WriteValues(members); 102 | } 103 | 104 | internal static CommandPacket SUnion(string[] keys) 105 | { 106 | return new CommandPacket("SUNION", CommandMode.Read | CommandMode.WithClientSideCache) 107 | .WriteKeys(keys); 108 | } 109 | 110 | internal static CommandPacket SUnionStore(string destination, string[] keys) 111 | { 112 | return new CommandPacket("SUNIONSTORE", CommandMode.Write) 113 | .WriteKey(destination) 114 | .WriteKeys(keys); 115 | } 116 | 117 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 118 | internal static CommandPacket SScan(string key, long cursor, string? pattern, ulong? count) 119 | #else 120 | internal static CommandPacket SScan(string key, long cursor, string pattern, ulong? count) 121 | #endif 122 | { 123 | return new CommandPacket("SSCAN", CommandMode.Read) 124 | .WriteKey(key) 125 | .WriteArg(cursor) 126 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 127 | .WriteArg(!string.IsNullOrEmpty(pattern), "MATCH", pattern!) 128 | #else 129 | .WriteArg(!string.IsNullOrEmpty(pattern), "MATCH", pattern) 130 | #endif 131 | .WriteArg(count.HasValue, "COUNT", count ?? 0); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/SharpRedis/Commands/TransactionCommands.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis.Models; 2 | 3 | namespace SharpRedis.Commands 4 | { 5 | internal static class TransactionCommands 6 | { 7 | static internal CommandPacket Discard() 8 | { 9 | return new CommandPacket("DISCARD", CommandMode.Transaction); 10 | } 11 | 12 | static internal CommandPacket Exec() 13 | { 14 | return new CommandPacket("EXEC", CommandMode.Transaction); 15 | } 16 | 17 | static internal CommandPacket Multi() 18 | { 19 | return new CommandPacket("MULTI", CommandMode.Transaction); 20 | } 21 | 22 | static internal CommandPacket Watch(params string[] keys) 23 | { 24 | return new CommandPacket("WATCH", CommandMode.Transaction) 25 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 26 | .WriteKeys(keys?.Length > 0, keys!); 27 | #else 28 | .WriteKeys(keys?.Length > 0, keys); 29 | #endif 30 | } 31 | 32 | static internal CommandPacket Unwatch() 33 | { 34 | return new CommandPacket("UNWATCH", CommandMode.Transaction); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/SharpRedis/Exceptions/RedisConnectionException.cs: -------------------------------------------------------------------------------- 1 | #if NET8_0 2 | #pragma warning disable IDE0290 3 | #endif 4 | using System; 5 | 6 | namespace SharpRedis 7 | { 8 | public sealed class RedisConnectionException : Exception 9 | { 10 | public RedisConnectionException(string msg) 11 | : base(msg) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SharpRedis/Exceptions/RedisException.cs: -------------------------------------------------------------------------------- 1 | #if NET8_0 2 | #pragma warning disable IDE0290 3 | #endif 4 | using System; 5 | 6 | namespace SharpRedis 7 | { 8 | public sealed class RedisException : Exception 9 | { 10 | public RedisException(string msg) 11 | : base(msg) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SharpRedis/Exceptions/RedisInitializationException.cs: -------------------------------------------------------------------------------- 1 | #if NET8_0 2 | #pragma warning disable IDE0290 3 | #endif 4 | using System; 5 | 6 | namespace SharpRedis 7 | { 8 | public sealed class RedisInitializationException : Exception 9 | { 10 | public RedisInitializationException(string msg) 11 | : base(msg) 12 | { 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/ConnectionExtensions.cs: -------------------------------------------------------------------------------- 1 | #if NET8_0_OR_GREATER 2 | #pragma warning disable IDE0300 3 | #endif 4 | using SharpRedis.Commands; 5 | using SharpRedis.Network.Standard; 6 | using System; 7 | 8 | namespace SharpRedis.Extensions 9 | { 10 | internal static class ConnectionExtensions 11 | { 12 | private static readonly string[] _separator = new string[] { "\r\n" }; 13 | 14 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 15 | internal static bool Auth(IConnection connection, string? user, string? password) 16 | #else 17 | internal static bool Auth(IConnection connection, string user, string password) 18 | #endif 19 | { 20 | if (string.IsNullOrEmpty(password)) return true; 21 | var result = connection.ExecuteCommand(ConnectionCommands.Auth(user, password), default); 22 | if (result is Exception ex) throw ex; 23 | if (result is byte[] bytes) 24 | { 25 | if ((bytes[0] == 79 || bytes[0] == 111) 26 | && (bytes[1] == 75 || bytes[1] == 107)) return true; 27 | } 28 | return false; 29 | } 30 | 31 | internal static string GetRedisVersion(IConnection connection) 32 | { 33 | var infoResult = connection.ExecuteCommand(ServerCommands.Info("server", "|", "grep", "redis_version"), default); 34 | if (infoResult is string serverInfo) 35 | { 36 | if (string.IsNullOrEmpty(serverInfo)) return string.Empty; 37 | var infoArray = serverInfo.Split(ConnectionExtensions._separator, StringSplitOptions.None); 38 | for (uint i = 0; i < infoArray.Length; i++) 39 | { 40 | if (infoArray[i].StartsWith("redis_version:", StringComparison.OrdinalIgnoreCase)) 41 | { 42 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 43 | return infoArray[i][14..]; 44 | #else 45 | return infoArray[i].Substring(14); 46 | #endif 47 | } 48 | } 49 | } 50 | return string.Empty; 51 | } 52 | 53 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 54 | internal static bool Hello(IConnection connection, RespVersion respVersion, string? user, string? password) 55 | #else 56 | internal static bool Hello(IConnection connection, RespVersion respVersion, string user, string password) 57 | #endif 58 | { 59 | var helloResult = connection.ExecuteCommand(ConnectionCommands.Hello(respVersion, user, password, connection.ConnectionName), default); 60 | if (helloResult is Exception ex) throw ex; 61 | if (helloResult != null && helloResult != DBNull.Value) 62 | { 63 | return true; 64 | } 65 | return false; 66 | } 67 | 68 | internal static bool SetClientName(IConnection connection) 69 | { 70 | var setNameResult = connection.ExecuteCommand(ConnectionCommands.ClientSetName(connection.ConnectionName), default); 71 | if (setNameResult is byte[] bytes) 72 | { 73 | if ((bytes[0] == 79 || bytes[0] == 111) 74 | && (bytes[1] == 75 || bytes[1] == 107)) return true; 75 | } 76 | return false; 77 | } 78 | 79 | internal static long ClientId(IConnection connection) 80 | { 81 | return ConvertExtensions.To(connection.ExecuteCommand(ConnectionCommands.ClientId(), default), ResultType.Int64, connection.Encoding); 82 | } 83 | 84 | /// 85 | /// Idle ping 86 | /// 87 | /// 88 | /// 89 | internal static bool Ping(IConnection connection) 90 | { 91 | try 92 | { 93 | var pingMsg = $"SharpRedis_{Guid.NewGuid():N}_Ping"; 94 | var pingResult = connection.ExecuteCommand(ConnectionCommands.Ping(pingMsg), default); 95 | var pintResultString = ConvertExtensions.To(pingResult, ResultType.String, connection.Encoding); 96 | if (pingMsg.Equals(pintResultString, StringComparison.OrdinalIgnoreCase)) 97 | { 98 | return true; 99 | } 100 | return false; 101 | } 102 | catch (Exception ex) 103 | { 104 | SharpConsole.WriteError($"Idle Exception, message: {ex.Message}"); 105 | return false; 106 | } 107 | } 108 | 109 | internal static bool Select(IConnection connection, ushort index) 110 | { 111 | var selectResult = connection.ExecuteCommand(ConnectionCommands.Select(index), default); 112 | if (selectResult is byte[] bytes) 113 | { 114 | if ((bytes[0] == 79 || bytes[0] == 111) 115 | && (bytes[1] == 75 || bytes[1] == 107)) return true; 116 | } 117 | return false; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/SharpConsole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharpRedis.Extensions 4 | { 5 | internal static class SharpConsole 6 | { 7 | private static readonly object _lock = new object(); 8 | 9 | internal static void WriteError(string errorMessage) 10 | { 11 | lock (SharpConsole._lock) 12 | { 13 | Console.BackgroundColor = ConsoleColor.Red; 14 | Console.ForegroundColor = ConsoleColor.White; 15 | Console.Write("Error: "); 16 | Console.BackgroundColor = ConsoleColor.Black; 17 | Console.ForegroundColor = ConsoleColor.Red; 18 | Console.WriteLine($" Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}. {errorMessage}"); 19 | Console.ForegroundColor = ConsoleColor.Gray; 20 | } 21 | } 22 | 23 | internal static void WriteInfo(string message) 24 | { 25 | lock (SharpConsole._lock) 26 | { 27 | Console.BackgroundColor = ConsoleColor.Green; 28 | Console.ForegroundColor = ConsoleColor.White; 29 | Console.Write("Info: "); 30 | Console.BackgroundColor = ConsoleColor.Black; 31 | Console.ForegroundColor = ConsoleColor.Green; 32 | Console.WriteLine($" Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}. {message}"); 33 | Console.ForegroundColor = ConsoleColor.Gray; 34 | } 35 | } 36 | 37 | internal static void WriteWarning(string warningMessage) 38 | { 39 | lock (SharpConsole._lock) 40 | { 41 | Console.BackgroundColor = ConsoleColor.Yellow; 42 | Console.ForegroundColor = ConsoleColor.Black; 43 | Console.Write("Warning: "); 44 | Console.BackgroundColor = ConsoleColor.Black; 45 | Console.ForegroundColor = ConsoleColor.Yellow; 46 | Console.WriteLine($" Time: {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}. {warningMessage}"); 47 | Console.ForegroundColor = ConsoleColor.Gray; 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharpRedis.Extensions 4 | { 5 | unsafe public static class StringExtensions 6 | { 7 | public static bool Matches(string input, params string[] patterns) 8 | { 9 | var _input = input?.Trim(); 10 | if (patterns is null || patterns.Length is 0 || string.IsNullOrEmpty(_input)) return false; 11 | fixed (char* pInput = _input) 12 | for (uint i = 0; i < patterns.Length; i++) 13 | fixed (char* pPattern = patterns[i]) 14 | if (StringExtensions.MatchesRecursive(pInput, pPattern)) return true; 15 | return false; 16 | } 17 | 18 | private static bool MatchesRecursive(char* input, char* pattern) 19 | { 20 | if (*input == '\0' && *pattern == '\0') return true; 21 | if (*pattern == '\0') return false; 22 | 23 | if (*input == '\0') 24 | { 25 | while (*pattern == '*') pattern++; 26 | return *pattern == '\0'; 27 | } 28 | 29 | if (*pattern == '*') 30 | { 31 | return StringExtensions.MatchesRecursive(input + 1, pattern) || StringExtensions.MatchesRecursive(input, pattern + 1); 32 | } 33 | else if (*pattern == '?') 34 | { 35 | return StringExtensions.MatchesRecursive(input + 1, pattern + 1); 36 | } 37 | else if (*pattern == '[') 38 | { 39 | char* closingBracket = pattern; 40 | while (*closingBracket != ']' && *closingBracket != '\0') closingBracket++; 41 | if (*closingBracket == '\0') throw new ArgumentException("Invalid pattern: missing ']'"); 42 | 43 | char* temp = pattern + 1; 44 | while (temp < closingBracket) 45 | { 46 | if (*temp == *input) return StringExtensions.MatchesRecursive(input + 1, closingBracket + 1); 47 | temp++; 48 | } 49 | return false; 50 | } 51 | else 52 | { 53 | return *input == *pattern && StringExtensions.MatchesRecursive(input + 1, pattern + 1); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net30/Action.cs: -------------------------------------------------------------------------------- 1 | #if NET30 2 | #pragma warning disable IDE0130 3 | namespace System 4 | { 5 | public delegate void Action(T1 arg1, T2 arg2); 6 | } 7 | #endif -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net30_35_40_45/Array.cs: -------------------------------------------------------------------------------- 1 | #if LOW_NET || NET40 || NET45 2 | #pragma warning disable IDE0130 3 | namespace SharpRedis 4 | { 5 | internal static class Array 6 | { 7 | public static T[] Empty() 8 | { 9 | return EmptyArray.Value; 10 | } 11 | } 12 | 13 | internal static class EmptyArray 14 | { 15 | public static readonly T[] Value = new T[0]; 16 | } 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net35_30/CancellationToken.cs: -------------------------------------------------------------------------------- 1 | #if LOW_NET 2 | #pragma warning disable IDE0130 3 | using System; 4 | using System.Threading; 5 | 6 | namespace SharpRedis 7 | { 8 | public readonly struct CancellationToken 9 | { 10 | private readonly CancellationTokenSource _source; 11 | 12 | public bool IsCancellationRequested 13 | { 14 | get 15 | { 16 | if (this._source is null) return false; 17 | return this._source.IsCancellationRequested; 18 | } 19 | } 20 | 21 | public WaitHandle WaitHandle 22 | { 23 | get 24 | { 25 | if (this._source == null) return CancellationTokenSource._neverCanceledSource.WaitHandle; 26 | return this._source.WaitHandle; 27 | } 28 | } 29 | 30 | public static CancellationToken None => default; 31 | 32 | internal CancellationToken(CancellationTokenSource source) 33 | { 34 | this._source = source; 35 | } 36 | 37 | public void ThrowIfCancellationRequested() 38 | { 39 | if (this.IsCancellationRequested) 40 | { 41 | throw new OperationCanceledException("The operation has timed out."); 42 | } 43 | } 44 | 45 | public override bool Equals(object obj) 46 | { 47 | if (obj is CancellationToken token) 48 | { 49 | return this.Equals(token); 50 | } 51 | return false; 52 | } 53 | 54 | public bool Equals(CancellationToken other) 55 | { 56 | if (this._source == null && other._source == null) return true; 57 | return this._source.Equals(other._source); 58 | } 59 | 60 | public override int GetHashCode() 61 | { 62 | return this._source.GetHashCode(); 63 | } 64 | 65 | public static bool operator ==(CancellationToken left, CancellationToken right) 66 | { 67 | return left.Equals(right); 68 | } 69 | 70 | public static bool operator !=(CancellationToken left, CancellationToken right) 71 | { 72 | return !(left == right); 73 | } 74 | } 75 | } 76 | #endif 77 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net35_30/CancellationTokenSource.cs: -------------------------------------------------------------------------------- 1 | #if LOW_NET 2 | #pragma warning disable IDE0130 3 | using System; 4 | using System.Threading; 5 | 6 | namespace SharpRedis 7 | { 8 | public sealed class CancellationTokenSource : IDisposable 9 | { 10 | private volatile bool _isCancellationRequested; 11 | private readonly CancellationToken _cancellationToken; 12 | private volatile Timer _timer; 13 | private volatile ManualResetEvent _kernelEvent; 14 | private bool _disposedValue; 15 | 16 | internal static readonly CancellationTokenSource _neverCanceledSource = new CancellationTokenSource(); 17 | 18 | public CancellationToken Token 19 | { 20 | get 21 | { 22 | if (this._disposedValue) throw new ObjectDisposedException(nameof(CancellationTokenSource)); 23 | return this._cancellationToken; 24 | } 25 | } 26 | 27 | public WaitHandle WaitHandle 28 | { 29 | get 30 | { 31 | if (this._disposedValue) throw new ObjectDisposedException(nameof(CancellationTokenSource)); 32 | if (this._kernelEvent != null) return this._kernelEvent; 33 | var mre = new ManualResetEvent(false); 34 | if (Interlocked.CompareExchange(ref this._kernelEvent, mre, null) != null) 35 | { 36 | (mre as IDisposable).Dispose(); 37 | } 38 | 39 | if (this.IsCancellationRequested) 40 | { 41 | this._kernelEvent.Set(); 42 | } 43 | return this._kernelEvent; 44 | } 45 | } 46 | 47 | public bool IsCancellationRequested => this._isCancellationRequested; 48 | 49 | public CancellationTokenSource() 50 | { 51 | this._cancellationToken = new CancellationToken(this); 52 | } 53 | 54 | public void Cancel() 55 | { 56 | if (this._disposedValue) throw new ObjectDisposedException(nameof(CancellationTokenSource)); 57 | lock (this) 58 | { 59 | if (this._isCancellationRequested) return; 60 | this._isCancellationRequested = true; 61 | this._kernelEvent?.Set(); 62 | this._timer?.Dispose(); 63 | this._timer = null; 64 | } 65 | } 66 | 67 | public void CancelAfter(int millisecondsDelay) 68 | { 69 | if (this._disposedValue) throw new ObjectDisposedException(nameof(CancellationTokenSource)); 70 | lock (this) 71 | { 72 | if (this._isCancellationRequested) return; 73 | this._timer?.Dispose(); 74 | this._timer = null; 75 | this._timer = new Timer(_ => this.Cancel(), null, millisecondsDelay, Timeout.Infinite); 76 | } 77 | } 78 | 79 | public void CancelAfter(TimeSpan dueTime) 80 | { 81 | if (this._disposedValue) throw new ObjectDisposedException(nameof(CancellationTokenSource)); 82 | lock (this) 83 | { 84 | if (this._isCancellationRequested) return; 85 | this._timer?.Dispose(); 86 | this._timer = null; 87 | this._timer = new Timer(_ => this.Cancel(), null, (int)dueTime.TotalMilliseconds, Timeout.Infinite); 88 | } 89 | } 90 | 91 | private void Dispose(bool disposing) 92 | { 93 | if (!this._disposedValue) 94 | { 95 | this._disposedValue = true; 96 | if (disposing) 97 | { 98 | if (this._kernelEvent != null) 99 | { 100 | var mre = Interlocked.Exchange(ref this._kernelEvent, null); 101 | if (mre != null) (mre as IDisposable).Dispose(); 102 | } 103 | this._timer?.Dispose(); 104 | } 105 | this._timer = null; 106 | } 107 | } 108 | 109 | public void Dispose() 110 | { 111 | this.Dispose(disposing: true); 112 | GC.SuppressFinalize(this); 113 | } 114 | 115 | ~CancellationTokenSource() 116 | { 117 | this.Dispose(disposing: false); 118 | } 119 | } 120 | } 121 | #endif -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net35_30/ConcurrentDictionary.cs: -------------------------------------------------------------------------------- 1 | #if LOW_NET 2 | #pragma warning disable IDE0130 3 | using System.Collections.Generic; 4 | 5 | namespace System.Collections.Concurrent 6 | { 7 | internal sealed class ConcurrentDictionary : IEnumerable> 8 | { 9 | private readonly int _numLocks; 10 | private readonly object[] _locks; 11 | private readonly Dictionary[] _dictionaries; 12 | 13 | internal int Count 14 | { 15 | get 16 | { 17 | int count = 0; 18 | for (int i = 0; i < _numLocks; i++) 19 | { 20 | lock (this._locks[i]) 21 | { 22 | count += this._dictionaries[i].Count; 23 | } 24 | } 25 | return count; 26 | } 27 | } 28 | 29 | internal bool IsEmpty 30 | { 31 | get 32 | { 33 | for (uint i = 0; i < this._numLocks; i++) 34 | { 35 | lock (this._locks[i]) 36 | { 37 | if (this._dictionaries[i].Count > 0) 38 | { 39 | return false; 40 | } 41 | } 42 | } 43 | return true; 44 | } 45 | } 46 | 47 | internal IEnumerable Keys 48 | { 49 | get 50 | { 51 | var keys = new List(); 52 | for (int i = 0; i < this._numLocks; i++) 53 | { 54 | lock (this._locks[i]) 55 | { 56 | keys.AddRange(this._dictionaries[i].Keys); 57 | } 58 | } 59 | return keys; 60 | } 61 | } 62 | 63 | internal IEnumerable Values 64 | { 65 | get 66 | { 67 | var values = new List(); 68 | for (int i = 0; i < this._numLocks; i++) 69 | { 70 | lock (this._locks[i]) 71 | { 72 | values.AddRange(this._dictionaries[i].Values); 73 | } 74 | } 75 | return values; 76 | } 77 | } 78 | 79 | internal ConcurrentDictionary(int concurrencyLevel = 16, int initialCapacity = 101) 80 | { 81 | this._numLocks = concurrencyLevel; 82 | this._locks = new object[this._numLocks]; 83 | this._dictionaries = new Dictionary[this._numLocks]; 84 | 85 | for (uint i = 0; i < this._numLocks; i++) 86 | { 87 | this._locks[i] = new object(); 88 | this._dictionaries[i] = new Dictionary(initialCapacity / _numLocks); 89 | } 90 | } 91 | 92 | private int GetLockIndex(TKey key) 93 | { 94 | return (key.GetHashCode() & 0x7FFFFFFF) % this._numLocks; 95 | } 96 | 97 | internal bool TryAdd(TKey key, TValue value) 98 | { 99 | int lockIndex = this.GetLockIndex(key); 100 | lock (this._locks[lockIndex]) 101 | { 102 | if (this._dictionaries[lockIndex].ContainsKey(key)) 103 | { 104 | return false; 105 | } 106 | this._dictionaries[lockIndex][key] = value; 107 | return true; 108 | } 109 | } 110 | 111 | internal bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue) 112 | { 113 | int lockIndex = this.GetLockIndex(key); 114 | lock (this._locks[lockIndex]) 115 | { 116 | if (this._dictionaries[lockIndex].TryGetValue(key, out var existingValue) && EqualityComparer.Default.Equals(existingValue, comparisonValue)) 117 | { 118 | this._dictionaries[lockIndex][key] = newValue; 119 | return true; 120 | } 121 | return false; 122 | } 123 | } 124 | 125 | internal bool TryRemove(TKey key, out TValue value) 126 | { 127 | int lockIndex = this.GetLockIndex(key); 128 | lock (this._locks[lockIndex]) 129 | { 130 | if (this._dictionaries[lockIndex].TryGetValue(key, out value)) 131 | { 132 | this._dictionaries[lockIndex].Remove(key); 133 | return true; 134 | } 135 | return false; 136 | } 137 | } 138 | 139 | internal bool TryGetValue(TKey key, out TValue value) 140 | { 141 | int lockIndex = this.GetLockIndex(key); 142 | lock (this._locks[lockIndex]) 143 | { 144 | return this._dictionaries[lockIndex].TryGetValue(key, out value); 145 | } 146 | } 147 | 148 | internal TValue this[TKey key] 149 | { 150 | get 151 | { 152 | if (this.TryGetValue(key, out var value)) 153 | { 154 | return value; 155 | } 156 | throw new KeyNotFoundException(); 157 | } 158 | set 159 | { 160 | int lockIndex = this.GetLockIndex(key); 161 | lock (this._locks[lockIndex]) 162 | { 163 | this._dictionaries[lockIndex][key] = value; 164 | } 165 | } 166 | } 167 | 168 | internal bool ContainsKey(TKey key) 169 | { 170 | return this.TryGetValue(key, out _); 171 | } 172 | 173 | internal void Clear() 174 | { 175 | for (int i = 0; i < _numLocks; i++) 176 | { 177 | lock (this._locks[i]) 178 | { 179 | this._dictionaries[i].Clear(); 180 | } 181 | } 182 | } 183 | 184 | public IEnumerator> GetEnumerator() 185 | { 186 | for (int i = 0; i < this._numLocks; i++) 187 | { 188 | lock (this._locks[i]) 189 | { 190 | foreach (var kvp in this._dictionaries[i]) 191 | { 192 | yield return kvp; 193 | } 194 | } 195 | } 196 | } 197 | 198 | IEnumerator IEnumerable.GetEnumerator() 199 | { 200 | return GetEnumerator(); 201 | } 202 | } 203 | } 204 | #endif 205 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net35_30/ConcurrentQueue.cs: -------------------------------------------------------------------------------- 1 | #if LOW_NET 2 | #pragma warning disable IDE0130 3 | using System.Threading; 4 | 5 | namespace System.Collections.Concurrent 6 | { 7 | internal sealed class ConcurrentQueue 8 | { 9 | private Node _head; 10 | private Node _tail; 11 | 12 | internal bool IsEmpty 13 | { 14 | get 15 | { 16 | return this._head._next == null; 17 | } 18 | } 19 | 20 | internal ConcurrentQueue() 21 | { 22 | this._head = new Node(default); 23 | this._tail = _head; 24 | } 25 | 26 | internal void Enqueue(T item) 27 | { 28 | var newNode = new Node(item); 29 | while (true) 30 | { 31 | Node oldTail = this._tail; 32 | Node oldTailNext = oldTail._next; 33 | 34 | if (this._tail == oldTail) 35 | { 36 | if (oldTailNext == null) 37 | { 38 | if (Interlocked.CompareExchange(ref oldTail._next, newNode, null) == null) 39 | { 40 | _ = Interlocked.CompareExchange(ref this._tail, newNode, oldTail); 41 | return; 42 | } 43 | } 44 | else 45 | { 46 | _ = Interlocked.CompareExchange(ref this._tail, oldTailNext, oldTail); 47 | } 48 | } 49 | } 50 | } 51 | 52 | internal bool TryDequeue(out T result) 53 | { 54 | result = default; 55 | while (true) 56 | { 57 | var oldHead = this._head; 58 | var oldTail = this._tail; 59 | var oldHeadNext = oldHead._next; 60 | 61 | if (oldHead == this._head) 62 | { 63 | if (oldHead == oldTail) 64 | { 65 | if (oldHeadNext == null) return false; 66 | _ = Interlocked.CompareExchange(ref this._tail, oldHeadNext, oldTail); 67 | } 68 | else 69 | { 70 | result = oldHeadNext._value; 71 | if (Interlocked.CompareExchange(ref this._head, oldHeadNext, oldHead) == oldHead) 72 | { 73 | return true; 74 | } 75 | } 76 | } 77 | } 78 | } 79 | 80 | internal bool TryPeek(out T result) 81 | { 82 | var oldHeadNext = this._head._next; 83 | if (oldHeadNext == null) 84 | { 85 | result = default; 86 | return false; 87 | } 88 | result = oldHeadNext._value; 89 | return true; 90 | } 91 | 92 | internal int Count 93 | { 94 | get 95 | { 96 | int count = 0; 97 | var current = this._head._next; 98 | while (current != null) 99 | { 100 | count++; 101 | current = current._next; 102 | } 103 | return count; 104 | } 105 | } 106 | 107 | internal void Clear() 108 | { 109 | this._head = new Node(default); 110 | this._tail = this._head; 111 | } 112 | 113 | private sealed class Node 114 | { 115 | internal T _value; 116 | internal Node _next; 117 | 118 | internal Node(T value) 119 | { 120 | this._value = value; 121 | this._next = null; 122 | } 123 | } 124 | } 125 | } 126 | #endif 127 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net35_30/ConcurrentStack.cs: -------------------------------------------------------------------------------- 1 | #if LOW_NET 2 | #pragma warning disable IDE0130 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | 6 | namespace System.Collections.Concurrent 7 | { 8 | internal sealed class ConcurrentStack 9 | { 10 | private Node _head = null; 11 | 12 | internal bool IsEmpty => this._head == null; 13 | 14 | internal ConcurrentStack() 15 | { 16 | } 17 | 18 | internal void Push(T item) 19 | { 20 | var newNode = new Node(item); 21 | while (true) 22 | { 23 | var oldHead = this._head; 24 | newNode._next = oldHead; 25 | if (Interlocked.CompareExchange(ref this._head, newNode, oldHead) == oldHead) 26 | { 27 | return; 28 | } 29 | } 30 | } 31 | 32 | internal bool TryPop(out T result) 33 | { 34 | while (true) 35 | { 36 | var oldHead = this._head; 37 | if (oldHead == null) 38 | { 39 | result = default; 40 | return false; 41 | } 42 | 43 | Node newHead = oldHead._next; 44 | if (Interlocked.CompareExchange(ref this._head, newHead, oldHead) == oldHead) 45 | { 46 | result = oldHead._value; 47 | return true; 48 | } 49 | } 50 | } 51 | 52 | internal bool TryPeek(out T result) 53 | { 54 | var oldHead = this._head; 55 | if (oldHead == null) 56 | { 57 | result = default; 58 | return false; 59 | } 60 | result = oldHead._value; 61 | return true; 62 | } 63 | 64 | internal int Count 65 | { 66 | get 67 | { 68 | int count = 0; 69 | var current = this._head; 70 | while (current != null) 71 | { 72 | count++; 73 | current = current._next; 74 | } 75 | return count; 76 | } 77 | } 78 | 79 | internal void Clear() 80 | { 81 | this._head = null; 82 | } 83 | 84 | internal T[] ToArray() 85 | { 86 | var list = new List(); 87 | var current = this._head; 88 | while (current != null) 89 | { 90 | list.Add(current._value); 91 | current = current._next; 92 | } 93 | list.Reverse(); 94 | return list.ToArray(); 95 | } 96 | 97 | private sealed class Node 98 | { 99 | internal T _value; 100 | internal Node _next; 101 | 102 | public Node(T value) 103 | { 104 | this._value = value; 105 | this._next = null; 106 | } 107 | } 108 | } 109 | } 110 | #endif 111 | -------------------------------------------------------------------------------- /src/SharpRedis/Extensions/net35_30/StructuralEqualityComparer.cs: -------------------------------------------------------------------------------- 1 | #if LOW_NET 2 | #pragma warning disable IDE0130 3 | namespace System.Collections.StructuralComparisons 4 | { 5 | internal static class StructuralEqualityComparer 6 | { 7 | static internal bool Equals(byte[] array1, byte[] array2) 8 | { 9 | if (array1 == array2) return true; 10 | 11 | if (array1 == null || array2 == null || array1.Length != array2.Length) 12 | return false; 13 | 14 | for (uint i = 0; i < array1.Length; i++) 15 | { 16 | if (array1[i] != array2[i]) 17 | return false; 18 | } 19 | return true; 20 | } 21 | } 22 | } 23 | #endif 24 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/BitFieldArg.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | /// 6 | /// BitField Arg 7 | /// 8 | public sealed class BitFieldArg : IBitFieldArg, IBitFieldArgEncoding>, IBitFieldArgOffset, //GET 9 | IBitFieldArgEncoding>, IBitFieldArgOffset, IBitFieldArgValue, //SET 10 | IBitFieldArgEncoding>, IBitFieldArgOffset, IBitFieldArgIncrement, //Increment 11 | IBitFieldArgOverflow //OVERFLOW 12 | { 13 | private readonly string[] _args; 14 | private int _currentIndex = 0; 15 | private readonly BitFieldArgType _type; 16 | 17 | BitFieldArgType IBitFieldArg.ArgType => this._type; 18 | 19 | private BitFieldArg(int argsSize, string argName, BitFieldArgType type) 20 | { 21 | this._args = new string[argsSize]; 22 | this.Write(argName); 23 | this._type = type; 24 | } 25 | 26 | #region Creates 27 | /// 28 | /// Create a GET arg 29 | /// 创建一个GET参数 30 | /// 31 | /// 32 | public static IBitFieldArgEncoding> CreateGet() 33 | { 34 | return new BitFieldArg(3, "GET", BitFieldArgType.Get); 35 | } 36 | 37 | /// 38 | /// Create a SET arg 39 | /// 创建一个SET参数 40 | /// 41 | /// 42 | public static IBitFieldArgEncoding> CreateSet() 43 | { 44 | return new BitFieldArg(4, "SET", BitFieldArgType.Set); 45 | } 46 | 47 | /// 48 | /// Create a OVERFLOW arg 49 | /// 创建一个OVERFLOW参数 50 | /// 51 | /// 52 | public static IBitFieldArgOverflow CreateOverflow() 53 | { 54 | return new BitFieldArg(2, "OVERFLOW", BitFieldArgType.Overflow); 55 | } 56 | 57 | /// 58 | /// Create a INCRBY arg 59 | /// 创建一个INCRBY参数 60 | /// 61 | /// 62 | public static IBitFieldArgEncoding> CreateIncrement() 63 | { 64 | return new BitFieldArg(4, "INCRBY", BitFieldArgType.Increment); 65 | } 66 | #endregion 67 | 68 | #region Get 69 | IBitFieldArgOffset IBitFieldArgEncoding>.Signed(uint u) 70 | { 71 | if (u <= 0) throw new RedisException("Minimum support 1"); 72 | if (u >= 65) throw new RedisException("Maximum support 64"); 73 | this.Write($"i{u}"); 74 | return this; 75 | } 76 | 77 | IBitFieldArgOffset IBitFieldArgEncoding>.Unsigned(uint u) 78 | { 79 | if (u <= 0) throw new RedisException("Minimum support 1"); 80 | if (u >= 64) throw new RedisException("Maximum support 63"); 81 | this.Write($"u{u}"); 82 | return this; 83 | } 84 | 85 | IBitFieldArg IBitFieldArgOffset.Offset(uint offset) 86 | { 87 | this.Write(offset.ToString()); 88 | return this; 89 | } 90 | 91 | IBitFieldArg IBitFieldArgOffset.MultipliedOffset(uint offset) 92 | { 93 | this.Write($"#{offset}"); 94 | return this; 95 | } 96 | #endregion 97 | 98 | #region Set 99 | IBitFieldArgOffset IBitFieldArgEncoding>.Signed(uint u) 100 | { 101 | if (u <= 0) throw new RedisException("Minimum support 1"); 102 | if (u >= 65) throw new RedisException("Maximum support 64"); 103 | this.Write($"i{u}"); 104 | return this; 105 | } 106 | 107 | IBitFieldArgOffset IBitFieldArgEncoding>.Unsigned(uint u) 108 | { 109 | if (u <= 0) throw new RedisException("Minimum support 1"); 110 | if (u >= 64) throw new RedisException("Maximum support 63"); 111 | this.Write($"u{u}"); 112 | return this; 113 | } 114 | 115 | IBitFieldArgValue IBitFieldArgOffset.Offset(uint offset) 116 | { 117 | this.Write(offset.ToString()); 118 | return this; 119 | } 120 | 121 | IBitFieldArgValue IBitFieldArgOffset.MultipliedOffset(uint offset) 122 | { 123 | this.Write($"#{offset}"); 124 | return this; 125 | } 126 | 127 | IBitFieldArg IBitFieldArgValue.Value(long value) 128 | { 129 | this.Write(value.ToString()); 130 | return this; 131 | } 132 | #endregion 133 | 134 | #region Overflow 135 | IBitFieldArg IBitFieldArgOverflow.Wrap() 136 | { 137 | this._args[this._currentIndex++] = "WRAP"; 138 | return this; 139 | } 140 | 141 | IBitFieldArg IBitFieldArgOverflow.Sat() 142 | { 143 | this._args[this._currentIndex++] = "SAT"; 144 | return this; 145 | } 146 | 147 | IBitFieldArg IBitFieldArgOverflow.Fail() 148 | { 149 | this._args[this._currentIndex++] = "FAIL"; 150 | return this; 151 | } 152 | #endregion 153 | 154 | #region Increment 155 | IBitFieldArgOffset IBitFieldArgEncoding>.Signed(uint u) 156 | { 157 | if (u <= 0) throw new RedisException("Minimum support 1"); 158 | if (u >= 65) throw new RedisException("Maximum support 64"); 159 | this.Write($"i{u}"); 160 | return this; 161 | } 162 | 163 | IBitFieldArgOffset IBitFieldArgEncoding>.Unsigned(uint u) 164 | { 165 | if (u <= 0) throw new RedisException("Minimum support 1"); 166 | if (u >= 64) throw new RedisException("Maximum support 63"); 167 | this.Write($"u{u}"); 168 | return this; 169 | } 170 | 171 | IBitFieldArgIncrement IBitFieldArgOffset.Offset(uint offset) 172 | { 173 | this.Write(offset.ToString()); 174 | return this; 175 | } 176 | 177 | IBitFieldArgIncrement IBitFieldArgOffset.MultipliedOffset(uint offset) 178 | { 179 | this.Write($"#{offset}"); 180 | return this; 181 | } 182 | 183 | IBitFieldArg IBitFieldArgIncrement.Increment(long increment) 184 | { 185 | this.Write(increment.ToString()); 186 | return this; 187 | } 188 | #endregion 189 | 190 | string[] IBitFieldArg.Convert() => this._args; 191 | 192 | private void Write(string arg) 193 | { 194 | this._args[this._currentIndex++] = arg; 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/ChannelMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharpRedis.Models 4 | { 5 | internal readonly struct ChannelMode : IEquatable 6 | { 7 | internal ChannelModeEnum Mode { get; } 8 | internal string Channel { get; } 9 | 10 | internal ChannelMode(string channel, in ChannelModeEnum mode) 11 | { 12 | this.Mode = mode; 13 | this.Channel = channel; 14 | } 15 | 16 | public override int GetHashCode() 17 | { 18 | return this.Channel.GetHashCode() ^ this.Mode.GetHashCode(); 19 | } 20 | 21 | public bool Equals(ChannelMode other) 22 | { 23 | return this.Channel == other.Channel && this.Mode == other.Mode; 24 | } 25 | 26 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 27 | public override bool Equals(object? obj) 28 | #else 29 | public override bool Equals(object obj) 30 | #endif 31 | { 32 | if (obj == null) return false; 33 | if (obj is ChannelMode cm) 34 | { 35 | return cm.Equals(this); 36 | } 37 | return false; 38 | } 39 | 40 | public override string ToString() 41 | { 42 | return this.Channel; 43 | } 44 | 45 | public static bool operator ==(ChannelMode left, ChannelMode right) 46 | { 47 | return left.Equals(right); 48 | } 49 | 50 | public static bool operator !=(ChannelMode left, ChannelMode right) 51 | { 52 | return !left.Equals(right); 53 | } 54 | 55 | public static implicit operator string(in ChannelMode cm) 56 | { 57 | return cm.Channel; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/ClientSideCacheKey.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | using System; 3 | 4 | namespace SharpRedis 5 | { 6 | public readonly struct ClientSideCacheKey : IEquatable 7 | { 8 | private readonly string[] _keys; 9 | private readonly long _keyHash; 10 | 11 | internal ClientSideCacheKey(string[] keys, long keyHash) 12 | { 13 | this._keys = keys; 14 | this._keyHash = keyHash; 15 | } 16 | 17 | internal bool ContainsKey(string key) 18 | { 19 | for (uint i = 0; i < this._keys.Length; i++) 20 | { 21 | if (this._keys[i] == key) return true; 22 | } 23 | return false; 24 | } 25 | 26 | internal string[] GetKeys() 27 | { 28 | return this._keys; 29 | } 30 | 31 | public override int GetHashCode() 32 | { 33 | return this._keyHash.GetHashCode(); 34 | } 35 | 36 | public bool Equals(ClientSideCacheKey other) 37 | { 38 | return other._keyHash == this._keyHash; 39 | } 40 | 41 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 42 | public override bool Equals(object? obj) 43 | #else 44 | public override bool Equals(object obj) 45 | #endif 46 | { 47 | if (obj == null) return false; 48 | if (obj is ClientSideCacheKey item) 49 | { 50 | return item.Equals(this); 51 | } 52 | return false; 53 | } 54 | 55 | public override string ToString() 56 | { 57 | return this._keyHash.ToString(); 58 | } 59 | 60 | public static bool operator ==(ClientSideCacheKey left, ClientSideCacheKey right) 61 | { 62 | return left.Equals(right); 63 | } 64 | 65 | public static bool operator !=(ClientSideCacheKey left, ClientSideCacheKey right) 66 | { 67 | return !left.Equals(right); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/Delegates.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 3 | #nullable enable 4 | #endif 5 | #if !LOW_NET 6 | using System.Threading.Tasks; 7 | #endif 8 | 9 | namespace SharpRedis 10 | { 11 | #region Sync NET3.0 NET3.5 12 | #if LOW_NET 13 | /// 14 | /// Receive subscription event 15 | /// 收到订阅消息事件 16 | /// 17 | /// Subscribed channel 18 | /// 订阅的通道名称 19 | /// 20 | /// Data received 21 | /// 收到的数据 22 | /// 23 | public delegate void OnReceive(string channel, object data); 24 | 25 | /// 26 | /// Receive subscription event 27 | /// 收到订阅消息事件 28 | /// 29 | /// Subscribes the client to the given patterns 30 | /// 订阅的模式 31 | /// 32 | /// Subscribed channel 33 | /// 订阅的通道名称 34 | /// 35 | /// Data received 36 | /// 收到的数据 37 | /// 38 | public delegate void POnReceive(string pattern, string channel, object data); 39 | #endif 40 | #endregion 41 | 42 | #region Async 43 | #if !LOW_NET 44 | /// 45 | /// Receive subscription event 46 | /// 收到订阅消息异步事件 47 | /// 48 | /// Subscribed channel 49 | /// 订阅的通道名称 50 | /// 51 | /// Data received 52 | /// 收到的数据 53 | /// 54 | public delegate Task OnReceive(string channel, object data); 55 | 56 | /// 57 | /// Receive subscription event 58 | /// 收到订阅消息异步事件 59 | /// 60 | /// Subscribes the client to the given patterns. 61 | /// Subscribed channel 62 | /// 订阅的通道名称 63 | /// 64 | /// 65 | /// Data received 66 | /// 收到的数据 67 | /// 68 | /// 69 | public delegate Task POnReceive(string pattern, string channel, object data); 70 | #endif 71 | #endregion 72 | } 73 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/OnReceiveModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharpRedis.Models 4 | { 5 | internal sealed class OnReceiveModel 6 | where TOnReceive : Delegate 7 | { 8 | private readonly ResultDataType _subscribeDataType; 9 | private readonly TOnReceive _onReceive; 10 | 11 | internal ResultDataType DataType => this._subscribeDataType; 12 | 13 | internal TOnReceive OnReceive => this._onReceive; 14 | 15 | internal OnReceiveModel(ResultDataType subscribeDataType, TOnReceive onReceive) 16 | { 17 | this._subscribeDataType = subscribeDataType; 18 | this._onReceive = onReceive; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/Standard/IBitFieldArg.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | /// 6 | /// BitField args 7 | /// 8 | public interface IBitFieldArg 9 | { 10 | /// 11 | /// Get arg type 12 | /// 获得参数类型 13 | /// 14 | BitFieldArgType ArgType { get; } 15 | 16 | /// 17 | /// Convert to args 18 | /// 19 | /// 20 | string[] Convert(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/Standard/IBitFieldArgEncoding.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | public interface IBitFieldArgEncoding 6 | { 7 | /// 8 | /// Writes an unsigned integer 9 | /// 写入无符号整数 10 | /// 11 | /// number 12 | /// 13 | T Unsigned(uint u); 14 | 15 | /// 16 | /// Writes an signed integer 17 | /// 写入有符号整数 18 | /// 19 | /// number 20 | /// 21 | T Signed(uint u); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/Standard/IBitFieldArgIncrement.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | public interface IBitFieldArgIncrement 6 | { 7 | /// 8 | /// Write number increment 9 | /// 写入自增值 10 | /// 11 | /// increment 12 | /// 13 | IBitFieldArg Increment(long increment); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/Standard/IBitFieldArgOffset.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | public interface IBitFieldArgOffset 6 | { 7 | /// 8 | /// Set offset 9 | /// 设置偏移量 10 | /// 11 | /// offset 12 | /// 13 | T Offset(uint offset); 14 | 15 | /// 16 | /// if the offset is prefixed with a # character, the specified offset is multiplied by the integer encoding's width 17 | /// 指定的偏移量将乘以整数编码的宽度 18 | /// 19 | /// offset 20 | /// 21 | T MultipliedOffset(uint offset); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/Standard/IBitFieldArgOverflow.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | public interface IBitFieldArgOverflow 6 | { 7 | /// 8 | /// Wrap around, both with signed and unsigned integers. In the case of unsigned integers, wrapping is like performing the operation modulo the maximum value the integer can contain (the C standard behavior). With signed integers instead wrapping means that overflows restart towards the most negative value and underflows towards the most positive ones, so for example if an i8 integer is set to the value 127, incrementing it by 1 will yield -128 9 | /// 环绕,有符号和无符号整数。对于无符号整数,换行就像执行对整数可以包含的最大值取模的运算(C 标准行为)。使用有符号整数而不是换行意味着上溢重新开始朝向最大负值,下溢朝向最大正值,因此例如,如果 i8 整数设置为值 127,则将其递增 1 将产生 -128 10 | /// 11 | /// 12 | IBitFieldArg Wrap(); 13 | 14 | /// 15 | /// Uses saturation arithmetic, that is, on underflows the value is set to the minimum integer value, and on overflows to the maximum integer value. For example incrementing an i8 integer starting from value 120 with an increment of 10, will result into the value 127, and further increments will always keep the value at 127. The same happens on underflows, but towards the value is blocked at the most negative value 16 | /// 使用饱和算术,即下溢时将值设置为最小整数值,上溢时将值设置为最大整数值。例如,从值 120 开始递增 i8 整数,增量为 10,将得到值 127,进一步的增量将始终使该值保持在 127。下溢时也会发生同样的情况,但下溢的方向是该值被阻止在最大负值 17 | /// 18 | /// 19 | IBitFieldArg Sat(); 20 | 21 | /// 22 | /// In this mode no operation is performed on overflows or underflows detected. The corresponding return value is set to NULL to signal the condition to the caller 23 | /// 在此模式下,检测到上溢或下溢时不执行任何操作。相应的返回值设置为 NULL 以向调用者发出信号通知 24 | /// 25 | /// 26 | IBitFieldArg Fail(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/Standard/IBitFieldArgValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | public interface IBitFieldArgValue 6 | { 7 | /// 8 | /// Write number value 9 | /// 写入数值 10 | /// 11 | /// value 12 | /// 13 | IBitFieldArg Value(long value); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/SharpRedis/Models/StreamTrimOptions.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | #if NET5_0_OR_GREATER 3 | #pragma warning disable IDE0090 4 | #endif 5 | namespace SharpRedis 6 | { 7 | public sealed class StreamTrimOptions : System.IEquatable 8 | { 9 | private readonly static StreamTrimOptions _empty = new StreamTrimOptions(); 10 | 11 | private readonly StreamTrimMode _trimMode; 12 | private readonly StreamTrimStrategy _trimStrategy; 13 | private readonly string _threshold; 14 | private readonly ulong _limit; 15 | 16 | internal StreamTrimMode TrimMode => this._trimMode; 17 | 18 | internal StreamTrimStrategy TrimStrategy => this._trimStrategy; 19 | 20 | internal string Threshold => this._threshold; 21 | 22 | internal ulong Limit => this._limit; 23 | 24 | /// 25 | /// Gets empty StreamTrimOptions 26 | /// 获得一个空的修剪配置 27 | /// 28 | public static StreamTrimOptions Empty => StreamTrimOptions._empty; 29 | 30 | private StreamTrimOptions() 31 | { 32 | this._threshold = string.Empty; 33 | this._trimMode = StreamTrimMode.None; 34 | this._trimStrategy = StreamTrimStrategy.None; 35 | } 36 | 37 | /// 38 | /// Create StreamTrimOptions 39 | /// 40 | /// MAXLEN | MINID 41 | /// = | ~ 42 | /// threshold 43 | /// limit count. The default 0 indicates no limit 44 | /// Available since: 6.2.0 45 | /// 删除个数, 默认0表示无限制 46 | /// 支持此参数的Redis版本: 6.2.0+ 47 | /// 48 | public StreamTrimOptions(StreamTrimMode trimMode, StreamTrimStrategy trimStrategy, string threshold, ulong limit = 0) 49 | { 50 | this._trimMode = trimMode; 51 | this._trimStrategy = trimStrategy; 52 | this._threshold = threshold; 53 | this._limit = limit; 54 | } 55 | 56 | public override string ToString() 57 | { 58 | if (this._limit > 0) 59 | { 60 | return $"{this._trimMode.ToString().ToUpper()} {this._trimStrategy.ToString().ToUpper()} {this._threshold} LIMIT {this._limit}"; 61 | } 62 | else 63 | { 64 | return $"{this._trimMode.ToString().ToUpper()} {this._trimStrategy.ToString().ToUpper()} {this._threshold}"; 65 | } 66 | } 67 | 68 | public override int GetHashCode() 69 | { 70 | return this._trimMode.GetHashCode() ^ this._trimStrategy.GetHashCode() ^ this._limit.GetHashCode() ^ this._threshold.GetHashCode(); 71 | } 72 | 73 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 74 | public override bool Equals(object? obj) 75 | #else 76 | public override bool Equals(object obj) 77 | #endif 78 | { 79 | if (obj is StreamTrimOptions other) 80 | { 81 | return other == this; 82 | } 83 | return false; 84 | } 85 | 86 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 87 | public bool Equals(StreamTrimOptions? other) 88 | #else 89 | public bool Equals(StreamTrimOptions other) 90 | #endif 91 | { 92 | if (other is null) return false; 93 | return this._threshold == other._threshold 94 | && this._limit == other._limit 95 | && this._trimMode == other._trimMode 96 | && this._trimStrategy == other._trimStrategy; 97 | } 98 | 99 | /// 100 | /// Create StreamTrimOptions 101 | /// 102 | /// MAXLEN | MINID 103 | /// = | ~ 104 | /// threshold 105 | /// limit count. The default 0 indicates no limit 106 | /// Available since: 6.2.0 107 | /// 删除个数, 默认0表示无限制 108 | /// 支持此参数的Redis版本: 6.2.0+ 109 | /// 110 | public static StreamTrimOptions Create(StreamTrimMode trimMode, StreamTrimStrategy trimStrategy, string threshold, ulong limit = 0) 111 | { 112 | return new StreamTrimOptions(trimMode, trimStrategy, threshold, limit); 113 | } 114 | 115 | public static bool operator ==(StreamTrimOptions left, StreamTrimOptions right) 116 | { 117 | return left.Equals(right); 118 | } 119 | 120 | public static bool operator !=(StreamTrimOptions left, StreamTrimOptions right) 121 | { 122 | return !left.Equals(right); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/SharpRedis/Network/DefaultConnection.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis.Network.Standard; 2 | using System.Text; 3 | 4 | namespace SharpRedis.Network 5 | { 6 | internal sealed class DefaultConnection : BaseConnection 7 | { 8 | private readonly int _slaveIndex; 9 | 10 | internal int SlaveIndex => this._slaveIndex; 11 | 12 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 13 | internal DefaultConnection(Encoding encoding, ConnectionOptions connectionOptions, byte[]? _prefix, int slaveIndex = -1) 14 | #else 15 | internal DefaultConnection(Encoding encoding, ConnectionOptions connectionOptions, byte[] _prefix, int slaveIndex = -1) 16 | #endif 17 | : base(encoding, connectionOptions, ConnectionType.Master, _prefix) 18 | { 19 | this._slaveIndex = slaveIndex; 20 | } 21 | 22 | ~DefaultConnection() 23 | { 24 | base.Dispose(true); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/SharpRedis/Network/Pool/DefaultPool.cs: -------------------------------------------------------------------------------- 1 | #if !LOW_NET 2 | using System.Threading.Tasks; 3 | using System.Threading; 4 | #endif 5 | using SharpRedis.Network.Standard; 6 | 7 | namespace SharpRedis.Network.Pool 8 | { 9 | internal sealed class DefaultPool : BaseConnectionPool 10 | { 11 | internal DefaultPool(ConnectionOptions masterConnectionOptions) 12 | : base(masterConnectionOptions) 13 | { 14 | base._idleThread.Start(); 15 | } 16 | 17 | public sealed override void ReturnSlaveConnection(DefaultConnection connection) 18 | { 19 | throw new System.NotSupportedException(); 20 | } 21 | 22 | public sealed override DefaultConnection GetSlaveConnection(CancellationToken cancellationToken) 23 | { 24 | throw new System.NotSupportedException(); 25 | } 26 | 27 | #if !LOW_NET 28 | public sealed override Task GetSlaveConnectionAsync(CancellationToken cancellationToken) 29 | { 30 | throw new System.NotSupportedException(); 31 | } 32 | #endif 33 | 34 | ~DefaultPool() 35 | { 36 | base.Dispose(true); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/SharpRedis/Network/Standard/IConnection.cs: -------------------------------------------------------------------------------- 1 | #if !LOW_NET 2 | using System.Threading.Tasks; 3 | using System.Threading; 4 | #endif 5 | using SharpRedis.Models; 6 | using System; 7 | using System.Text; 8 | 9 | namespace SharpRedis.Network.Standard 10 | { 11 | internal interface IConnection : IDisposable 12 | { 13 | #region Properties 14 | bool Connected { get; } 15 | 16 | ConnectionOptions ConnectionOptions { get; } 17 | 18 | ConnectionType Type { get; } 19 | 20 | long ConnectionId { get; } 21 | 22 | string ConnectionName { get; } 23 | 24 | DateTime LastActiveTime { get; } 25 | 26 | ushort CurrentDataBaseIndex { get; } 27 | 28 | bool Tracking { get; set; } 29 | 30 | long RedirectConnectionId { get; set; } 31 | 32 | Encoding Encoding { get; } 33 | #endregion 34 | 35 | #region Methods 36 | void ResetBuffer(); 37 | 38 | bool Connect(); 39 | 40 | bool SwitchDatabaseIndex(ushort databaseIndex); 41 | 42 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 43 | object? ExecuteCommand(CommandPacket command, CancellationToken cancellationToken); 44 | 45 | object? ExecuteCommands(CommandPacket[] commands, CancellationToken cancellationToken); 46 | 47 | Task ExecuteCommandAsync(CommandPacket command, CancellationToken cancellationToken); 48 | 49 | Task ExecuteCommandsAsync(CommandPacket[] commands, CancellationToken cancellationToken); 50 | #else 51 | object ExecuteCommand(CommandPacket command, CancellationToken cancellationToken); 52 | 53 | object ExecuteCommands(CommandPacket[] commands, CancellationToken cancellationToken); 54 | 55 | #if !LOW_NET 56 | Task ExecuteCommandAsync(CommandPacket command, CancellationToken cancellationToken); 57 | 58 | Task ExecuteCommandsAsync(CommandPacket[] commands, CancellationToken cancellationToken); 59 | #endif 60 | #endif 61 | #endregion 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/SharpRedis/Network/Standard/IConnectionPool.cs: -------------------------------------------------------------------------------- 1 | #if !LOW_NET 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | #endif 5 | using System; 6 | using System.Text; 7 | 8 | 9 | namespace SharpRedis.Network.Standard 10 | { 11 | internal interface IConnectionPool : IDisposable 12 | { 13 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 14 | byte[]? KeyPrefix { get; } 15 | #else 16 | byte[] KeyPrefix { get; } 17 | #endif 18 | 19 | Encoding Encoding { get; } 20 | 21 | ConnectionOptions MasterConnectionOptions { get; } 22 | 23 | #region Methods 24 | DefaultConnection GetMasterConnection(CancellationToken cancellationToken); 25 | 26 | DefaultConnection GetSlaveConnection(CancellationToken cancellationToken); 27 | #if !LOW_NET 28 | Task GetMasterConnectionAsync(CancellationToken cancellationToken); 29 | 30 | Task GetSlaveConnectionAsync(CancellationToken cancellationToken); 31 | #endif 32 | 33 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 34 | DefaultConnection? CreateMasterConnection(); 35 | 36 | SubConnection? CreateSubConnection(); 37 | #else 38 | DefaultConnection CreateMasterConnection(); 39 | 40 | SubConnection CreateSubConnection(); 41 | #endif 42 | 43 | void ReturnMasterConnection(DefaultConnection connection); 44 | 45 | void ReturnSlaveConnection(DefaultConnection connection); 46 | 47 | IConnection[] GetAllMasterConnections(); 48 | 49 | SubConnection GetSubConnection(); 50 | 51 | SubConnection[] GetAllSubConnections(); 52 | 53 | IConnection[] GetAllConnections(); 54 | 55 | void SetSetClientSideCaching(ClientSideCachingStandard clientSideCaching); 56 | #endregion 57 | 58 | #region Idle methods 59 | void IdleWithHeartbeat(); 60 | 61 | void IdleMasterConnections(int delayMilliseconds); 62 | 63 | void IdleSubConnections(); 64 | 65 | void IdleSlaveConnections(int delayMilliseconds); 66 | #endregion 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Calls/DefaultCall.cs: -------------------------------------------------------------------------------- 1 | #if !LOW_NET 2 | using System.Threading.Tasks; 3 | using System.Threading; 4 | #endif 5 | using SharpRedis.Models; 6 | using SharpRedis.Provider.Standard; 7 | using System; 8 | using SharpRedis.Network.Standard; 9 | 10 | namespace SharpRedis.Provider.Calls 11 | { 12 | internal sealed class DefaultCall : BaseCall 13 | { 14 | internal sealed override bool SubUsable => true; 15 | 16 | internal sealed override string CallMode => "Default mode"; 17 | 18 | internal DefaultCall(IConnectionPool connectionPool) 19 | : base(connectionPool) 20 | { 21 | } 22 | 23 | #region Sync 24 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 25 | sealed internal override object? Call(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 26 | #else 27 | sealed internal override object Call(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 28 | #endif 29 | { 30 | var connection = base.ConnectionPool.GetMasterConnection(cancellationToken); 31 | try 32 | { 33 | return connection.ExecuteCommand(command, cancellationToken); 34 | } 35 | catch 36 | { 37 | throw; 38 | } 39 | finally 40 | { 41 | base.ConnectionPool.ReturnMasterConnection(connection); 42 | } 43 | } 44 | 45 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 46 | internal sealed override object?[]? Calls(CommandPacket[] commands, CancellationToken cancellationToken) 47 | #else 48 | internal sealed override object[] Calls(CommandPacket[] commands, CancellationToken cancellationToken) 49 | #endif 50 | { 51 | var connection = base.ConnectionPool.GetMasterConnection(cancellationToken); 52 | try 53 | { 54 | var result = connection.ExecuteCommands(commands, cancellationToken); 55 | if (result is Exception ex) throw ex; 56 | if (result is object[] array) 57 | { 58 | if (array.Length != commands.Length) throw new RedisException("The number of pipe return values is inconsistent"); 59 | return array; 60 | } 61 | throw new RedisException("Not a valid pipe return value"); 62 | } 63 | catch 64 | { 65 | throw; 66 | } 67 | finally 68 | { 69 | base.ConnectionPool.ReturnMasterConnection(connection); 70 | } 71 | } 72 | #endregion 73 | 74 | #region Async 75 | #if !LOW_NET 76 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 77 | sealed async internal override Task CallAsync(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 78 | #else 79 | sealed async internal override Task CallAsync(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 80 | #endif 81 | { 82 | var connection = await base.ConnectionPool.GetMasterConnectionAsync(cancellationToken).ConfigureAwait(false); 83 | try 84 | { 85 | return await connection.ExecuteCommandAsync(command, cancellationToken).ConfigureAwait(false); 86 | } 87 | catch 88 | { 89 | throw; 90 | } 91 | finally 92 | { 93 | base.ConnectionPool.ReturnMasterConnection(connection); 94 | } 95 | } 96 | 97 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 98 | async internal sealed override Task CallsAsync(CommandPacket[] commands, CancellationToken cancellationToken) 99 | #else 100 | async internal sealed override Task CallsAsync(CommandPacket[] commands, CancellationToken cancellationToken) 101 | #endif 102 | { 103 | var connection = await base.ConnectionPool.GetMasterConnectionAsync(cancellationToken).ConfigureAwait(false); 104 | try 105 | { 106 | var result = await connection.ExecuteCommandsAsync(commands, cancellationToken).ConfigureAwait(false); 107 | if (result is Exception ex) throw ex; 108 | if (result is object[] array) 109 | { 110 | if (array.Length != commands.Length) throw new RedisException("The number of pipe return values is inconsistent"); 111 | return array; 112 | } 113 | throw new RedisException("Not a valid pipe return value"); 114 | } 115 | catch 116 | { 117 | throw; 118 | } 119 | finally 120 | { 121 | base.ConnectionPool.ReturnMasterConnection(connection); 122 | } 123 | } 124 | #endif 125 | #endregion 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Calls/MasterSlaveCall.cs: -------------------------------------------------------------------------------- 1 | #if !LOW_NET 2 | using System.Threading.Tasks; 3 | using System.Threading; 4 | #endif 5 | using SharpRedis.Models; 6 | using SharpRedis.Provider.Standard; 7 | using System; 8 | using SharpRedis.Network.Standard; 9 | using SharpRedis.Network; 10 | 11 | namespace SharpRedis.Provider.Calls 12 | { 13 | internal sealed class MasterSlaveCall : BaseCall 14 | { 15 | internal MasterSlaveCall(IConnectionPool connectionPool) 16 | : base(connectionPool) 17 | { 18 | } 19 | 20 | internal sealed override bool SubUsable => true; 21 | 22 | internal sealed override string CallMode => "Master slave mode"; 23 | 24 | #region Sync 25 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 26 | internal sealed override object? Call(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 27 | #else 28 | internal sealed override object Call(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 29 | #endif 30 | { 31 | DefaultConnection connection; 32 | if ((command.Mode & CommandMode.Read) == CommandMode.Read) 33 | { 34 | connection = base.ConnectionPool.GetSlaveConnection(cancellationToken); 35 | } 36 | else 37 | { 38 | connection = base.ConnectionPool.GetMasterConnection(cancellationToken); 39 | } 40 | try 41 | { 42 | return connection.ExecuteCommand(command, cancellationToken); 43 | } 44 | catch 45 | { 46 | throw; 47 | } 48 | finally 49 | { 50 | if ((command.Mode & CommandMode.Read) == CommandMode.Read) 51 | base.ConnectionPool.ReturnSlaveConnection(connection); 52 | else 53 | base.ConnectionPool.ReturnMasterConnection(connection); 54 | } 55 | } 56 | 57 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 58 | internal sealed override object?[]? Calls(CommandPacket[] commands, CancellationToken cancellationToken) 59 | #else 60 | internal sealed override object[] Calls(CommandPacket[] commands, CancellationToken cancellationToken) 61 | #endif 62 | { 63 | var connection = base.ConnectionPool.GetMasterConnection(cancellationToken); 64 | try 65 | { 66 | var result = connection.ExecuteCommands(commands, cancellationToken); 67 | if (result is Exception ex) throw ex; 68 | if (result is object[] array) 69 | { 70 | if (array.Length != commands.Length) throw new RedisException("The number of pipe return values is inconsistent"); 71 | return array; 72 | } 73 | throw new RedisException("Not a valid pipe return value"); 74 | } 75 | catch 76 | { 77 | throw; 78 | } 79 | finally 80 | { 81 | base.ConnectionPool.ReturnMasterConnection(connection); 82 | } 83 | } 84 | #endregion 85 | 86 | #region Async 87 | #if !LOW_NET 88 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 89 | async sealed internal override Task CallAsync(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 90 | #else 91 | async sealed internal override Task CallAsync(CommandPacket command, ResultType resultType, CancellationToken cancellationToken) 92 | #endif 93 | { 94 | DefaultConnection connection; 95 | if ((command.Mode & CommandMode.Read) == CommandMode.Read) 96 | { 97 | connection = await base.ConnectionPool.GetSlaveConnectionAsync(cancellationToken).ConfigureAwait(false); 98 | } 99 | else 100 | { 101 | connection = await base.ConnectionPool.GetMasterConnectionAsync(cancellationToken).ConfigureAwait(false); 102 | } 103 | try 104 | { 105 | return await connection.ExecuteCommandAsync(command, cancellationToken).ConfigureAwait(false); 106 | } 107 | catch 108 | { 109 | throw; 110 | } 111 | finally 112 | { 113 | if ((command.Mode & CommandMode.Read) == CommandMode.Read) 114 | base.ConnectionPool.ReturnSlaveConnection(connection); 115 | else 116 | base.ConnectionPool.ReturnMasterConnection(connection); 117 | } 118 | } 119 | 120 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 121 | async sealed internal override Task CallsAsync(CommandPacket[] commands, CancellationToken cancellationToken) 122 | #else 123 | async sealed internal override Task CallsAsync(CommandPacket[] commands, CancellationToken cancellationToken) 124 | #endif 125 | { 126 | var connection = await base.ConnectionPool.GetMasterConnectionAsync(cancellationToken).ConfigureAwait(false); 127 | try 128 | { 129 | var result = await connection.ExecuteCommandsAsync(commands, cancellationToken).ConfigureAwait(false); 130 | if (result is Exception ex) throw ex; 131 | if (result is object[] array) 132 | { 133 | if (array.Length != commands.Length) throw new RedisException("The number of pipe return values is inconsistent"); 134 | return array; 135 | } 136 | throw new RedisException("Not a valid pipe return value"); 137 | } 138 | catch 139 | { 140 | throw; 141 | } 142 | finally 143 | { 144 | base.ConnectionPool.ReturnMasterConnection(connection); 145 | } 146 | } 147 | #endif 148 | #endregion 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/RedisSwitchDatabase.cs: -------------------------------------------------------------------------------- 1 | #if !NET30 2 | #pragma warning disable IDE0130 3 | #endif 4 | using SharpRedis.Provider.Calls; 5 | using SharpRedis.Provider.Standard; 6 | 7 | namespace SharpRedis 8 | { 9 | /// 10 | /// Switches to the specified database client 11 | /// 切换到指定的数据库 12 | /// 13 | public sealed partial class RedisSwitchDatabase : FeatureRedis 14 | { 15 | private SwitchDatabaseCall _switchCall; 16 | 17 | internal SwitchDatabaseCall SwitchCall => this._switchCall; 18 | 19 | private protected sealed override string DisposedException => "The currently selected database connection has been released"; 20 | 21 | internal RedisSwitchDatabase(SwitchDatabaseCall call) : base(call) 22 | { 23 | this._switchCall = call; 24 | } 25 | 26 | protected sealed override void Dispose(bool disposing) 27 | { 28 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 29 | #pragma warning disable CS8625 30 | #endif 31 | base.Dispose(disposing); 32 | this._switchCall = null; 33 | } 34 | 35 | ~RedisSwitchDatabase() 36 | { 37 | this.Dispose(true); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Standard/BaseType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SharpRedis.Provider.Standard 4 | { 5 | public abstract class BaseType: IDisposable 6 | { 7 | private protected BaseCall _call; 8 | private bool _disposedValue; 9 | 10 | internal BaseType(BaseCall call) 11 | { 12 | this._call = call; 13 | } 14 | 15 | internal void SetCall(BaseCall call) 16 | { 17 | this._call = call; 18 | } 19 | 20 | protected virtual void Dispose(bool disposing) 21 | { 22 | if (!this._disposedValue) 23 | { 24 | this._disposedValue = true; 25 | if (disposing) 26 | { 27 | } 28 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 29 | #pragma warning disable CS8625 30 | #endif 31 | this._call = null; 32 | } 33 | } 34 | 35 | public void Dispose() 36 | { 37 | this.Dispose(disposing: true); 38 | GC.SuppressFinalize(this); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Standard/FeatureRedis.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis.Network.Standard; 2 | using System; 3 | 4 | namespace SharpRedis.Provider.Standard 5 | { 6 | public abstract class FeatureRedis : BaseRedis 7 | where T : FeatureRedis 8 | { 9 | private protected abstract string DisposedException { get; } 10 | 11 | internal sealed override IConnectionPool ConnectionPool => base._call.ConnectionPool; 12 | 13 | /// 14 | /// Gets an object of type Redis connection 15 | /// 获得操作Redis连接的对象 16 | /// 17 | public override RedisConnection Connection 18 | { 19 | get 20 | { 21 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 22 | return base._connection; 23 | } 24 | } 25 | 26 | /// 27 | /// Get the subscribe publish action object 28 | /// 获得Redis订阅发布操作对象 29 | /// 30 | public override RedisPubSub PubSub 31 | { 32 | get 33 | { 34 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 35 | return base._pubsub; 36 | } 37 | } 38 | 39 | /// 40 | /// Gets an object of type Redis String 41 | /// 获得操作Redis String类型的对象 42 | /// 43 | public override RedisString String 44 | { 45 | get 46 | { 47 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 48 | return base._string; 49 | } 50 | } 51 | 52 | /// 53 | /// Gets an object of type Redis Hash 54 | /// 获得Redis Hash类型操作对象 55 | /// 56 | public override RedisHash Hash 57 | { 58 | get 59 | { 60 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 61 | return base._hash; 62 | } 63 | } 64 | 65 | /// 66 | /// Gets an object of type Redis List 67 | /// 获得Redis List类型操作对象 68 | /// 69 | public override RedisList List 70 | { 71 | get 72 | { 73 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 74 | return base._list; 75 | } 76 | } 77 | 78 | /// 79 | /// Gets an object of type Redis Bitmap 80 | /// 获得Redis Bitmap类型操作对象 81 | /// 82 | public override RedisBitmap Bitmap 83 | { 84 | get 85 | { 86 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 87 | return base._bitmap; 88 | } 89 | } 90 | 91 | /// 92 | /// Gets an object of type Redis Set 93 | /// 获得Redis Set类型操作对象 94 | /// 95 | public override RedisSet Set 96 | { 97 | get 98 | { 99 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 100 | return base._set; 101 | } 102 | } 103 | 104 | /// 105 | /// Gets an object of type Redis Sorted Set 106 | /// 获得Redis Sorted Set类型操作对象 107 | /// 108 | public override RedisSortedSet SortedSet 109 | { 110 | get 111 | { 112 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 113 | return base._sortedSet; 114 | } 115 | } 116 | 117 | /// 118 | /// Gets an object of type Redis Stream 119 | /// 获得Redis Stream类型操作对象 120 | /// 121 | public override RedisStream Stream 122 | { 123 | get 124 | { 125 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 126 | return base._stream; 127 | } 128 | } 129 | 130 | /// 131 | /// Gets an object of type Redis HyperLogLog 132 | /// 获得Redis HyperLogLog类型操作对象 133 | /// 134 | public override RedisHyperLogLog HyperLogLog 135 | { 136 | get 137 | { 138 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 139 | return base._hyperLogLog; 140 | } 141 | } 142 | 143 | /// 144 | /// Gets an object of type Redis Geospatial Indices 145 | /// 获得Redis Geospatial Indices类型操作对象 146 | /// 147 | public override RedisGeospatialIndices Geospatial 148 | { 149 | get 150 | { 151 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 152 | return base._geospatial; 153 | } 154 | } 155 | 156 | /// 157 | /// Gets an object of type Redis lua script 158 | /// 获得Redis Lua脚本操作对象 159 | /// 160 | public override RedisScript Script 161 | { 162 | get 163 | { 164 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 165 | return base._script; 166 | } 167 | } 168 | 169 | /// 170 | /// Gets an object of type Redis Key 171 | /// 获得Redis Key操作对象 172 | /// 173 | public override RedisKey Key 174 | { 175 | get 176 | { 177 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 178 | return base._key; 179 | } 180 | } 181 | 182 | /// 183 | /// Gets an object of type Redis Server 184 | /// 获得Redis Server端操作对象 185 | /// 186 | public override RedisServer Server 187 | { 188 | get 189 | { 190 | if (base._disposedValue) throw new ObjectDisposedException(typeof(T).Name, this.DisposedException); 191 | return base._server; 192 | } 193 | } 194 | 195 | private protected FeatureRedis(BaseCall call) : base(call) 196 | { 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Types/Hash/RedisHashSpan.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 2 | using SharpRedis.Extensions; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpRedis 8 | { 9 | public sealed partial class RedisHash 10 | { 11 | /// 12 | /// Sets the specified fields to their respective values in the hash stored at key. 13 | /// This command overwrites the values of specified fields that exist in the hash. If key doesn't exist, a new key holding a hash is created. 14 | /// Available since: 2.0.0 15 | /// 将一个指定的字段的值存入Hash中 16 | /// 此命令会覆盖Hash中存在的指定字段的值。如果 key 不存在,则创建一个包含哈希的新键。 17 | /// 支持此命令的Redis版本, 2.0.0+ 18 | /// 19 | /// Hash keyHash的key 20 | /// fieldHash中的字段名 21 | /// value 22 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 23 | /// 取消Token, 有效值会覆盖系统默认的 24 | /// 25 | /// The number of fields that were added 26 | /// 成功添加的field数. 如果field已经存在会覆盖值, 但不计数 27 | /// 28 | public long HSet(string key, string field, ReadOnlySpan value, CancellationToken cancellationToken = default) 29 | { 30 | return this.HSet(key, field, value.SpanToBytes(base._call.Encoding), cancellationToken); 31 | } 32 | 33 | /// 34 | /// Sets the specified fields to their respective values in the hash stored at key. 35 | /// This command overwrites the values of specified fields that exist in the hash. If key doesn't exist, a new key holding a hash is created. 36 | /// Available since: 2.0.0 37 | /// 将一个指定的字段的值存入Hash中 38 | /// 此命令会覆盖Hash中存在的指定字段的值。如果 key 不存在,则创建一个包含哈希的新键。 39 | /// 支持此命令的Redis版本, 2.0.0+ 40 | /// 41 | /// Hash keyHash的key 42 | /// fieldHash中的字段名 43 | /// value 44 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 45 | /// 取消Token, 有效值会覆盖系统默认的 46 | /// 47 | /// The number of fields that were added 48 | /// 成功添加的field数. 如果field已经存在会覆盖值, 但不计数 49 | /// 50 | public Task HSetAsync(string key, string field, ReadOnlySpan value, CancellationToken cancellationToken = default) 51 | { 52 | return this.HSetAsync(key, field, value.SpanToBytes(base._call.Encoding), cancellationToken); 53 | } 54 | 55 | /// 56 | /// Sets field in the hash stored at key to value, only if field does not yet exist. If key does not exist, a new key holding a hash is created. If field already exists, this operation has no effect. 57 | /// Available since: 2.0.0 58 | /// 仅在指定Key的Hash中不存在指定的field时才设置值, 否则不设置 59 | /// 支持此命令的Redis版本, 2.0.0+ 60 | /// 61 | /// Hash key 62 | /// field 63 | /// value 64 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 65 | /// 取消Token, 有效值会覆盖系统默认的 66 | /// 67 | /// 68 | public bool HSetNx(string key, string field, ReadOnlySpan value, CancellationToken cancellationToken = default) 69 | { 70 | return this.HSetNx(key, field, value.SpanToBytes(base._call.Encoding), cancellationToken); 71 | } 72 | 73 | /// 74 | /// Sets field in the hash stored at key to value, only if field does not yet exist. If key does not exist, a new key holding a hash is created. If field already exists, this operation has no effect. 75 | /// Available since: 2.0.0 76 | /// 仅在指定Key的Hash中不存在指定的field时才设置值, 否则不设置 77 | /// 支持此命令的Redis版本, 2.0.0+ 78 | /// 79 | /// Hash key 80 | /// field 81 | /// value 82 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 83 | /// 取消Token, 有效值会覆盖系统默认的 84 | /// 85 | /// 86 | public Task HSetNxAsync(string key, string field, ReadOnlySpan value, CancellationToken cancellationToken = default) 87 | { 88 | return this.HSetNxAsync(key, field, value.SpanToBytes(base._call.Encoding), cancellationToken); 89 | } 90 | } 91 | } 92 | #endif 93 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Types/HyperLogLog/RedisHyperLogLogSpan.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 2 | using SharpRedis.Extensions; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpRedis 8 | { 9 | public sealed partial class RedisHyperLogLog 10 | { 11 | /// 12 | /// Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first argument 13 | /// Available since: 2.8.9 14 | /// 添加一个元素到指定的HyperLogLog中 15 | /// 支持此命令的Redis版本, 2.8.9+ 16 | /// 17 | /// Key 18 | /// element 19 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 20 | /// 取消Token, 有效值会覆盖系统默认的 21 | /// 22 | /// true: if at least one HyperLogLog internal register was altered 23 | /// false: if no HyperLogLog internal registers were altered 24 | /// true: 至少有一个元素被更改 25 | /// false: 没有元素被更改 26 | /// 27 | public bool PFAdd(string key, ReadOnlySpan element, CancellationToken cancellationToken = default) 28 | { 29 | return this.PFAdd(key, element.SpanToBytes(base._call.Encoding), cancellationToken); 30 | } 31 | 32 | /// 33 | /// Adds all the element arguments to the HyperLogLog data structure stored at the variable name specified as first argument 34 | /// Available since: 2.8.9 35 | /// 添加一个元素到指定的HyperLogLog中 36 | /// 支持此命令的Redis版本, 2.8.9+ 37 | /// 38 | /// Key 39 | /// element 40 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 41 | /// 取消Token, 有效值会覆盖系统默认的 42 | /// 43 | /// true: if at least one HyperLogLog internal register was altered 44 | /// false: if no HyperLogLog internal registers were altered 45 | /// true: 至少有一个元素被更改 46 | /// false: 没有元素被更改 47 | /// 48 | public Task PFAddAsync(string key, ReadOnlySpan element, CancellationToken cancellationToken = default) 49 | { 50 | return this.PFAddAsync(key, element.SpanToBytes(base._call.Encoding), cancellationToken); 51 | } 52 | } 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Types/PubSub/RedisPubSubSpan.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 2 | using SharpRedis.Extensions; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpRedis 8 | { 9 | public sealed partial class RedisPubSub 10 | { 11 | /// 12 | /// Posts a message to the given channel. 13 | /// Available since:2.0.0 14 | /// 向指定的通道发送消息 15 | /// 支持的Redis版本: 2.0.0+ 16 | /// 17 | /// Channel通道 18 | /// Message 19 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 20 | /// 取消Token, 有效值会覆盖系统默认的 21 | /// 22 | /// the number of clients that received the message. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count. 23 | /// 接受消息的客户端数量. 在集群模式中, 只有与发布客户端连接到同一个节点的客户端才包括在计数中。 24 | /// 25 | public long Publish(string channel, ReadOnlySpan message, CancellationToken cancellationToken = default) 26 | { 27 | return this.Publish(channel, message.SpanToBytes(base._call.Encoding), cancellationToken); 28 | } 29 | 30 | /// 31 | /// Posts a message to the given channel. 32 | /// Available since:2.0.0 33 | /// 向指定的通道发送消息 34 | /// 支持的Redis版本: 2.0.0+ 35 | /// 36 | /// Channel通道 37 | /// Message 38 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 39 | /// 取消Token, 有效值会覆盖系统默认的 40 | /// 41 | /// the number of clients that received the message. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count. 42 | /// 接受消息的客户端数量. 在集群模式中, 只有与发布客户端连接到同一个节点的客户端才包括在计数中。 43 | /// 44 | public Task PublishAsync(string channel, ReadOnlySpan message, CancellationToken cancellationToken = default) 45 | { 46 | return this.PublishAsync(channel, message.SpanToBytes(base._call.Encoding), cancellationToken); 47 | } 48 | 49 | /// 50 | /// Posts a message to the given shard channel. 51 | /// Available since:7.0.0 52 | /// 向给定的分片通道发布消息 53 | /// 支持的Redis版本: 7.0.0+ 54 | /// 55 | /// Shard channel 56 | /// 分片通道名称 57 | /// 58 | /// Message 59 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 60 | /// 取消Token, 有效值会覆盖系统默认的 61 | /// 62 | /// The number of clients that received the message. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count 63 | /// 接收到消息的客户端数量。在集群模式中,只有与发布客户端连接到同一节点的客户端才会被计算在内 64 | /// 65 | public long SPublish(string shardChannel, ReadOnlySpan message, CancellationToken cancellationToken = default) 66 | { 67 | return this.SPublish(shardChannel, message.SpanToBytes(base._call.Encoding), cancellationToken); 68 | } 69 | 70 | /// 71 | /// Posts a message to the given shard channel. 72 | /// Available since:7.0.0 73 | /// 向给定的分片通道发布消息 74 | /// 支持的Redis版本: 7.0.0+ 75 | /// 76 | /// Shard channel 77 | /// 分片通道名称 78 | /// 79 | /// Message 80 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 81 | /// 取消Token, 有效值会覆盖系统默认的 82 | /// 83 | /// The number of clients that received the message. Note that in a Redis Cluster, only clients that are connected to the same node as the publishing client are included in the count 84 | /// 接收到消息的客户端数量。在集群模式中,只有与发布客户端连接到同一节点的客户端才会被计算在内 85 | /// 86 | public Task SPublishAsync(string shardChannel, ReadOnlySpan message, CancellationToken cancellationToken = default) 87 | { 88 | return this.SPublishAsync(shardChannel, message.SpanToBytes(base._call.Encoding), cancellationToken); 89 | } 90 | } 91 | } 92 | #endif 93 | -------------------------------------------------------------------------------- /src/SharpRedis/Provider/Types/Stream/RedisStreamSpan.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 2 | using SharpRedis.Extensions; 3 | using System; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace SharpRedis 8 | { 9 | public sealed partial class RedisStream 10 | { 11 | /// 12 | /// Appends the specified stream entry to the stream at the specified key 13 | /// Available since: 5.0.0 14 | /// 将内容添加到指定Key的Stream中 15 | /// 支持此命令的Redis版本, 5.0.0+ 16 | /// 17 | /// Stream key 18 | /// field 19 | /// value 20 | /// The first part of the unique ID, the timestamp. If null is passed, Redis will automatically generate a unique ID 21 | /// 唯一ID的第一部分, 时间戳, 毫秒级. 如果传入null, Redis将自动生成唯一ID 22 | /// 23 | /// The second part of the unique ID is a serial number. If null is passed, Redis will automatically generate the serial number. Redis7.0.0 and later support automatic serial number generation 24 | /// 唯一ID的第二部分, 是一个序列号. 如果传入null, Redis将自动生成序列号. Redis7.0.0及以上版本才支持自动生成序列号 25 | /// 26 | /// If the key does not exist, as a side effect of running this command the key is created with a stream value. The creation of stream's key can be disabled with the NOMKSTREAM option 27 | /// Available since: 6.2.0 28 | /// 是否禁用Key不存在的时候创建Stream. 默认不禁止. 需要Redis 6.2.0+才支持设置为true 29 | /// 30 | /// Stream Trim Options 31 | /// Stream修剪参数, 默认不修剪 32 | /// 33 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 34 | /// 取消Token, 有效值会覆盖系统默认的 35 | /// 36 | /// A unique ID that is automatically generated. Null reply: if the NOMKSTREAM option is given and the key doesn't exist 37 | /// 自动生成的唯一ID. 如果NOMKSTREAM设置为true, 则Key不存在的时候返回null 38 | /// 39 | public string? XAdd(string key, string field, ReadOnlySpan value, ulong? milliseconds = null, ulong? sequenceNumber = null, bool nomkStream = false, StreamTrimOptions? trimOptions = null, CancellationToken cancellationToken = default) 40 | { 41 | return this.XAdd(key, field, value.SpanToBytes(base._call.Encoding), milliseconds, sequenceNumber, nomkStream, trimOptions, cancellationToken); 42 | } 43 | 44 | /// 45 | /// Appends the specified stream entry to the stream at the specified key 46 | /// Available since: 5.0.0 47 | /// 将内容添加到指定Key的Stream中 48 | /// 支持此命令的Redis版本, 5.0.0+ 49 | /// 50 | /// Stream key 51 | /// field 52 | /// value 53 | /// The first part of the unique ID, the timestamp. If null is passed, Redis will automatically generate a unique ID 54 | /// 唯一ID的第一部分, 时间戳, 毫秒级. 如果传入null, Redis将自动生成唯一ID 55 | /// 56 | /// The second part of the unique ID is a serial number. If null is passed, Redis will automatically generate the serial number. Redis7.0.0 and later support automatic serial number generation 57 | /// 唯一ID的第二部分, 是一个序列号. 如果传入null, Redis将自动生成序列号. Redis7.0.0及以上版本才支持自动生成序列号 58 | /// 59 | /// If the key does not exist, as a side effect of running this command the key is created with a stream value. The creation of stream's key can be disabled with the NOMKSTREAM option 60 | /// Available since: 6.2.0 61 | /// 是否禁用Key不存在的时候创建Stream. 默认不禁止. 需要Redis 6.2.0+才支持设置为true 62 | /// 63 | /// Stream Trim Options 64 | /// Stream修剪参数, 默认不修剪 65 | /// 66 | /// Propagates notification that operations should be canceled. The valid value overrides the system Token 67 | /// 取消Token, 有效值会覆盖系统默认的 68 | /// 69 | /// A unique ID that is automatically generated. Null reply: if the NOMKSTREAM option is given and the key doesn't exist 70 | /// 自动生成的唯一ID. 如果NOMKSTREAM设置为true, 则Key不存在的时候返回null 71 | /// 72 | public Task XAddAsync(string key, string field, ReadOnlySpan value, ulong? milliseconds = null, ulong? sequenceNumber = null, bool nomkStream = false, StreamTrimOptions? trimOptions = null, CancellationToken cancellationToken = default) 73 | { 74 | return this.XAddAsync(key, field, value.SpanToBytes(base._call.Encoding), milliseconds, sequenceNumber, nomkStream, trimOptions, cancellationToken); 75 | } 76 | } 77 | } 78 | #endif 79 | -------------------------------------------------------------------------------- /src/SharpRedis/SharpRedis.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net30;net35;net40;net45;net46;net47;net48;netstandard2.0;netstandard2.1;net5.0;net6.0;net7.0;net8.0;net9.0 5 | True 6 | SharpRedis 7 | SharpRedis 8 | 0.0.8.7 9 | Kwong 10 | A pure asynchronous driver of high performance high throughput Redis driver for C# 11 | SharpRedis;sharpredis;Redis;redis;Redics;RedisClient;RedisHelper;csredis;csharpredis;redisclients;C#Redis;Kwong;kwong;q7164518 12 | false 13 | true 14 | True 15 | Kwong 16 | True 17 | https://github.com/q7164518/SharpRedis 18 | https://github.com/q7164518/SharpRedis 19 | git 20 | Performance optimization, support. NET9 21 | 22 | 23 | enable 24 | 25 | 26 | enable 27 | 28 | 29 | enable 30 | 31 | 32 | enable 33 | 34 | 35 | enable 36 | 37 | 38 | enable 39 | 40 | 41 | 1701;1702;CS1591 42 | 43 | 44 | 1701;1702;CS1591 45 | 46 | 47 | 1701;1702;CS1591 48 | 49 | 50 | 1701;1702;CS1591 51 | 52 | 53 | 1701;1702;CS1591 54 | 55 | 56 | 1701;1702;CS1591 57 | 58 | 59 | 1701;1702;CS1591 60 | 61 | 62 | 1701;1702;CS1591 63 | 64 | 65 | 1701;1702;CS1591 66 | 67 | 68 | 1701;1702;CS1591 69 | 70 | 71 | 1701;1702;CS1591 72 | 73 | 74 | 1701;1702;CS1591 75 | 76 | 77 | 1701;1702;CS1591 78 | 79 | 80 | 1701;1702;CS1591 81 | 82 | 83 | 1701;1702;CS1591 84 | 85 | 86 | 1701;1702;CS1591 87 | 88 | 89 | 1701;1702;CS1591 90 | 91 | 92 | 1701;1702;CS1591 93 | 94 | 95 | 1701;1702;CS1591 96 | 97 | 98 | 1701;1702;CS1591 99 | 100 | 101 | 1701;1702;CS1591 102 | 103 | 104 | 1701;1702;CS1591 105 | 106 | 107 | 1701;1702;CS1591 108 | 109 | 110 | 1701;1702;CS1591 111 | 112 | 113 | 1701;1702;CS1591 114 | 115 | 116 | 1701;1702;CS1591 117 | 118 | 119 | 1701;1702;CS1591 120 | 121 | 122 | 1701;1702;CS1591 123 | 124 | 125 | 126 | LOW_NET 127 | 128 | 129 | LOW_NET 130 | 131 | 132 | 133 | 134 | 1.0.168 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/BooleanValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | using System; 3 | 4 | namespace SharpRedis 5 | { 6 | public readonly struct BooleanValue 7 | { 8 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 9 | private readonly string? _boolString; 10 | 11 | internal BooleanValue(string? boolString) 12 | #else 13 | private readonly string _boolString; 14 | 15 | internal BooleanValue(string boolString) 16 | #endif 17 | { 18 | this._boolString = boolString; 19 | } 20 | 21 | public bool ToBoolean() 22 | { 23 | if (this._boolString is null) return false; 24 | if (this._boolString.Equals("t", StringComparison.OrdinalIgnoreCase)) return true; 25 | if (this._boolString.Equals("f", StringComparison.OrdinalIgnoreCase)) return false; 26 | throw new FormatException($"\"{this._boolString}\" is not a valid Boolean value"); 27 | } 28 | 29 | public override string ToString() 30 | { 31 | return this.ToBoolean().ToString(); 32 | } 33 | 34 | public static implicit operator bool(BooleanValue value) => value.ToBoolean(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/CoordinateValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | using SharpRedis.Extensions; 3 | using System; 4 | using System.Text; 5 | 6 | namespace SharpRedis 7 | { 8 | /// 9 | /// Latitude and Longitude 10 | /// 经纬度 11 | /// 12 | public readonly struct CoordinateValue : System.IEquatable 13 | { 14 | private readonly double _longitude; 15 | private readonly double _latitude; 16 | 17 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 18 | /// 19 | /// Get longitude 20 | /// 获得经度 21 | /// 22 | public readonly double Longitude => this._longitude; 23 | 24 | /// 25 | /// Get latitude 26 | /// 获得纬度 27 | /// 28 | public readonly double Latitude => this._latitude; 29 | #else 30 | /// 31 | /// Get longitude 32 | /// 获得经度 33 | /// 34 | public double Longitude => this._longitude; 35 | 36 | /// 37 | /// Get latitude 38 | /// 获得纬度 39 | /// 40 | public double Latitude => this._latitude; 41 | #endif 42 | 43 | /// 44 | /// Create a latitude and longitude value 45 | /// 创建一个经纬度值 46 | /// 47 | /// longitude经度 48 | /// latitude纬度 49 | public CoordinateValue(double longitude, double latitude) 50 | { 51 | this._longitude = longitude; 52 | this._latitude = latitude; 53 | } 54 | 55 | internal CoordinateValue(object[] array, Encoding encoding) 56 | { 57 | if (array[0] is object[] pos && pos.Length is 2) 58 | { 59 | this._longitude = ConvertExtensions.To(pos[0], ResultType.Double, encoding); 60 | this._latitude = ConvertExtensions.To(pos[1], ResultType.Double, encoding); 61 | } 62 | else if (array.Length is 2) 63 | { 64 | this._longitude = ConvertExtensions.To(array[0], ResultType.Double, encoding); 65 | this._latitude = ConvertExtensions.To(array[1], ResultType.Double, encoding); 66 | } 67 | else 68 | { 69 | throw new FormatException($"The data is not a valid CoordinateValue, The actual type is {array.GetType().FullName}"); 70 | } 71 | } 72 | 73 | public override int GetHashCode() 74 | { 75 | return this._longitude.GetHashCode() ^ this._latitude.GetHashCode(); 76 | } 77 | 78 | public override string ToString() 79 | { 80 | return $"Longitude: {this._longitude}, Latitude: {this._latitude}"; 81 | } 82 | 83 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 84 | public override bool Equals(object? obj) 85 | #else 86 | public override bool Equals(object obj) 87 | #endif 88 | { 89 | if (obj is CoordinateValue other) 90 | { 91 | return other == this; 92 | } 93 | return false; 94 | } 95 | 96 | public bool Equals(CoordinateValue other) 97 | { 98 | return other._latitude == this._latitude && other._longitude == this._longitude; 99 | } 100 | 101 | /// 102 | /// Deconstruct, var (longitude, latitude) = this 103 | /// 解构函数var (longitude, latitude) = this 104 | /// 105 | /// longitude经度 106 | /// latitude纬度 107 | public void Deconstruct(out double longitude, out double latitude) 108 | { 109 | longitude = this._longitude; 110 | latitude = this._latitude; 111 | } 112 | 113 | public static bool operator ==(CoordinateValue left, CoordinateValue right) 114 | { 115 | return left.Equals(right); 116 | } 117 | 118 | public static bool operator !=(CoordinateValue left, CoordinateValue right) 119 | { 120 | return !left.Equals(right); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/GeoRadiusValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | /// 6 | /// [GEORADIUS] result 7 | /// [GEORADIUS]返回值 8 | /// 9 | public sealed class GeoRadiusValue 10 | where TMember : class 11 | { 12 | private readonly TMember _member; 13 | private readonly double? _dist; 14 | private readonly ulong? _hash; 15 | private readonly CoordinateValue? _coordinate; 16 | 17 | /// 18 | /// Get member 19 | /// 获得成员 20 | /// 21 | public TMember Member => this._member; 22 | 23 | /// 24 | /// Get distance 25 | /// 获得相差距离 26 | /// 27 | public double? Dist => this._dist; 28 | 29 | /// 30 | /// Also return the raw geohash-encoded sorted set score of the item, in the form of a 52 bit unsigned integer 31 | /// 获得以52位无符号整数的形式返回该项的原始geohash编码的排序集分数 32 | /// 33 | public ulong? Hash => this._hash; 34 | 35 | /// 36 | /// Get the latitude and longitude coordinates 37 | /// 获得经纬度坐标 38 | /// 39 | public CoordinateValue? Coordinate => this._coordinate; 40 | 41 | internal GeoRadiusValue(TMember member, double? dist, ulong? hash, CoordinateValue? coordinate) 42 | { 43 | this._member = member; 44 | this._dist = dist; 45 | this._hash = hash; 46 | this._coordinate = coordinate; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/MemberScoreValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | using SharpRedis.Extensions; 3 | using System; 4 | using System.Text; 5 | 6 | namespace SharpRedis 7 | { 8 | /// 9 | /// SortedSet member: value 10 | /// 11 | /// Member type 12 | public readonly struct MemberScoreValue 13 | where TMember : class 14 | { 15 | private readonly TMember _member; 16 | private readonly NumberValue _score; 17 | 18 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 19 | /// 20 | /// Get member 21 | /// 获得元素 22 | /// 23 | public readonly TMember Member => this._member; 24 | 25 | /// 26 | /// Get member score 27 | /// 获得元素排序分数 28 | /// 29 | public readonly NumberValue Score => this._score; 30 | #else 31 | /// 32 | /// Get member 33 | /// 获得元素 34 | /// 35 | public TMember Member => this._member; 36 | 37 | /// 38 | /// Get member score 39 | /// 获得元素排序分数 40 | /// 41 | public NumberValue Score => this._score; 42 | #endif 43 | 44 | internal MemberScoreValue(object data, Encoding encoding, ResultType memberType) 45 | { 46 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 47 | object? scoreObject = null, memberObject = null; 48 | #else 49 | object scoreObject = null, memberObject = null; 50 | #endif 51 | if (data is object[] array) 52 | { 53 | if (array[0] is object[] arr && arr.Length == 2 && array.Length == 1) 54 | { 55 | scoreObject = arr[1]; 56 | memberObject = arr[0]; 57 | } 58 | else 59 | { 60 | if (array.Length == 2) 61 | { 62 | scoreObject = array[1]; 63 | memberObject = array[0]; 64 | } 65 | } 66 | } 67 | 68 | if (scoreObject != null && memberObject != null) 69 | { 70 | var score = ConvertExtensions.To(scoreObject, ResultType.Number, encoding) 71 | ?? throw new FormatException($"The data is not a valid MemberScoreValue, The actual type is {data.GetType().FullName}"); 72 | var member = ConvertExtensions.To(memberObject, memberType, encoding) 73 | ?? throw new FormatException($"The data is not a valid MemberScoreValue, The actual type is {data.GetType().FullName}"); 74 | 75 | this._member = member; 76 | this._score = score; 77 | } 78 | else 79 | { 80 | throw new FormatException($"The data is not a valid MemberScoreValue, The actual type is {data.GetType().FullName}"); 81 | } 82 | } 83 | 84 | public override string ToString() 85 | { 86 | return $"{this._member}: {this._score}"; 87 | } 88 | 89 | /// 90 | /// Deconstruct, var (member, score) = this 91 | /// 解构函数, var (member, score) = this 92 | /// 93 | /// member 94 | /// score排序分数 95 | public void Deconstruct(out TMember member, out NumberValue score) 96 | { 97 | member = this.Member; 98 | score = this.Score; 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/ScanValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | /// 6 | /// SCAN result model 7 | /// SCAN返回结果类 8 | /// 9 | public sealed class ScanValue 10 | where TData : class, System.Collections.ICollection 11 | { 12 | private readonly ulong _cursor; 13 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 14 | private readonly TData? _data; 15 | 16 | /// 17 | /// Get the result returned by SCAN 18 | /// 获得SCAN返回的结果 19 | /// 20 | public TData? Data => this._data; 21 | #else 22 | private readonly TData _data; 23 | 24 | /// 25 | /// Get the result returned by SCAN 26 | /// 获得SCAN返回的结果 27 | /// 28 | public TData Data => this._data; 29 | #endif 30 | 31 | /// 32 | /// Get the cursor returned by SCAN 33 | /// 获得SCAN返回的游标 34 | /// 35 | public ulong Cursor => this._cursor; 36 | 37 | /// 38 | /// Whether it has ended 39 | /// 是否已结束 40 | /// 41 | public bool IsEnd => this._cursor == 0; 42 | 43 | /// 44 | /// Gets the count of the result returned by SCAN 45 | /// 获得SCAN返回的结果长度 46 | /// 47 | public int Count 48 | { 49 | get 50 | { 51 | if (this._data is null) return -1; 52 | return this._data.Count; 53 | } 54 | } 55 | 56 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 57 | internal ScanValue(ulong cursor, TData? data) 58 | #else 59 | internal ScanValue(ulong cursor, TData data) 60 | #endif 61 | { 62 | this._cursor = cursor; 63 | this._data = data; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/ScoreRankValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | /// 6 | /// SortedSet score: rank 7 | /// 8 | /// 排序分数类型, 只能是数字类型 9 | public readonly struct ScoreRankValue 10 | { 11 | private readonly long _rank; 12 | private readonly NumberValue _score; 13 | 14 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 15 | /// 16 | /// Get rank 17 | /// 获得排名 18 | /// 19 | public readonly long Rank => this._rank; 20 | 21 | /// 22 | /// Get score 23 | /// 获得排序分数 24 | /// 25 | public readonly NumberValue Score => this._score; 26 | #else 27 | /// 28 | /// Get rank 29 | /// 获得排名 30 | /// 31 | public long Rank => this._rank; 32 | 33 | /// 34 | /// Get score 35 | /// 获得排序分数 36 | /// 37 | public NumberValue Score => this._score; 38 | #endif 39 | 40 | /// 41 | /// SortedSet score: rank 42 | /// 43 | /// rank排名 44 | /// score排序分数 45 | internal ScoreRankValue(long rank, NumberValue score) 46 | { 47 | this._rank = rank; 48 | this._score = score; 49 | } 50 | 51 | public override string ToString() 52 | { 53 | return $"{this._rank}: {this._score}"; 54 | } 55 | 56 | /// 57 | /// Deconstruct, var (rank, score) = this 58 | /// 解构函数, var (rank, score) = this 59 | /// 60 | /// rank排名 61 | /// score排序分数 62 | public void Deconstruct(out long rank, out NumberValue score) 63 | { 64 | rank = this.Rank; 65 | score = this.Score; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/StreamValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | using SharpRedis.Extensions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace SharpRedis 8 | { 9 | public sealed class StreamValue 10 | where TValue : class 11 | { 12 | private readonly string _id; 13 | 14 | /// 15 | /// Gets ID 16 | /// 获得内容条目ID 17 | /// 18 | public string ID => this._id; 19 | 20 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 21 | private readonly KeyValuePair[]? _fvArray; 22 | 23 | /// 24 | /// Gets the entry, which is a key-value array, where Key represents field and value represents Value 25 | /// 获得内容条目, 是一个键值对数组, 键表示field, 值表示value 26 | /// 27 | public KeyValuePair[]? FieldValue => this._fvArray; 28 | #else 29 | private readonly KeyValuePair[] _fvArray; 30 | 31 | /// 32 | /// Gets the entry, which is a key-value array, where Key represents field and value represents Value 33 | /// 获得内容条目, 是一个键值对数组, 键表示field, 值表示value 34 | /// 35 | public KeyValuePair[] FieldValue => this._fvArray; 36 | #endif 37 | 38 | internal StreamValue(object data, Encoding encoding, ResultType valueType) 39 | { 40 | if (data is object[] array && array.Length is 2) 41 | { 42 | var id = ConvertExtensions.To(array[0], ResultType.String, encoding) 43 | ?? throw new FormatException($"The data is not a valid StreamValue, The actual type is {data.GetType().FullName}"); 44 | this._id = id; 45 | 46 | var fvs = ConvertExtensions.To[]>(array[1], ResultType.KeyValuePairArray | valueType, encoding) 47 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 48 | ! 49 | #endif 50 | ; 51 | this._fvArray = fvs; 52 | return; 53 | } 54 | 55 | if (data is byte[]) 56 | { 57 | var id = ConvertExtensions.To(data, ResultType.String, encoding) 58 | ?? throw new FormatException($"The data is not a valid StreamValue, The actual type is {data.GetType().FullName}"); 59 | this._id = id; 60 | return; 61 | } 62 | throw new FormatException($"The data is not a valid StreamValue, The actual type is {data.GetType().FullName}"); 63 | } 64 | 65 | public override int GetHashCode() 66 | { 67 | if (this._fvArray != null) 68 | { 69 | return this._id.GetHashCode() ^ this._fvArray.GetHashCode(); 70 | } 71 | return this._id.GetHashCode(); 72 | } 73 | 74 | /// 75 | /// Deconstruct, var (id, fieldValue) = this 76 | /// 解构函数, var (id, fieldValue) = this 77 | /// 78 | /// id 79 | /// fieldValue 80 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 81 | public void Deconstruct(out string id, out KeyValuePair[]? fieldValue) 82 | #else 83 | public void Deconstruct(out string id, out KeyValuePair[] fieldValue) 84 | #endif 85 | { 86 | id = this._id; 87 | fieldValue = this._fvArray; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/TransactionValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | using System; 3 | 4 | namespace SharpRedis 5 | { 6 | /// 7 | /// Transaction return value 8 | /// 执行事务的返回值 9 | /// 10 | public sealed class TransactionValue 11 | { 12 | private readonly bool _discarded; 13 | /// 14 | /// Whether the transaction was discarded and not executed. 15 | /// 事务是否被丢弃且未执行 16 | /// 17 | public bool Discarded => this._discarded; 18 | 19 | 20 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 21 | private readonly object?[]? _values; 22 | private readonly object?[]? _queued; 23 | private readonly Exception? _discardedException; 24 | #else 25 | private readonly object[] _values; 26 | private readonly object[] _queued; 27 | private readonly Exception _discardedException; 28 | #endif 29 | 30 | /// 31 | /// Gets the transaction execution return value 32 | /// 获取事务执行的返回值 33 | /// 34 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 35 | public object?[]? Values => this._values; 36 | #else 37 | public object[] Values => this._values; 38 | #endif 39 | 40 | /// 41 | /// Gets the return value of the command added to the queue 42 | /// 获取命令入列的返回值 43 | /// 44 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 45 | public object? Queued => this._queued; 46 | #else 47 | public object Queued => this._queued; 48 | #endif 49 | 50 | /// 51 | /// Get the exception that the transaction discarded 52 | /// 获得事务被丢弃的异常对象 53 | /// 54 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 55 | public Exception? DiscardedException => this._discardedException; 56 | #else 57 | public Exception DiscardedException => this._discardedException; 58 | #endif 59 | 60 | 61 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 62 | internal TransactionValue(object?[]? values, object?[]? _queued, bool discarded, Exception? exception) 63 | #else 64 | internal TransactionValue(object[] values, object[] _queued, bool discarded, Exception exception) 65 | #endif 66 | { 67 | this._values = values; 68 | this._queued = _queued; 69 | this._discarded = discarded; 70 | this._discardedException = exception; 71 | } 72 | 73 | public static implicit operator bool(TransactionValue value) 74 | { 75 | return !value._discarded && value._values != null; 76 | } 77 | 78 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 79 | public static implicit operator object?[]?(TransactionValue value) 80 | #else 81 | public static implicit operator object[](TransactionValue value) 82 | #endif 83 | { 84 | return value._values; 85 | } 86 | 87 | /// 88 | /// Deconstruct 89 | /// 90 | /// 91 | /// 92 | /// 93 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 94 | public void Deconstruct(out bool ok, out object?[]? values, out object?[]? queued) 95 | #else 96 | public void Deconstruct(out bool ok, out object[] values, out object[] queued) 97 | #endif 98 | { 99 | ok = !this._discarded && this._values != null; 100 | values = this._values; 101 | queued = this._queued; 102 | } 103 | 104 | /// 105 | /// Deconstruct 106 | /// 107 | /// 108 | /// 109 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 110 | public void Deconstruct(out object?[]? values, out object?[]? queued) 111 | #else 112 | public void Deconstruct(out object[] values, out object[] queued) 113 | #endif 114 | { 115 | values = this._values; 116 | queued = this._queued; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/XAutoClaimValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | 3 | namespace SharpRedis 4 | { 5 | public sealed class XAutoClaimValue 6 | where TValue : class 7 | { 8 | private readonly string _nextID; 9 | 10 | /// 11 | /// A stream ID to be used as the start argument for the next call to XAUTOCLAIM 12 | /// 用于下次调用XAUTOCLAIM的ID 13 | /// 14 | public string NextID => this._nextID; 15 | 16 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 17 | private readonly StreamValue[]? _fvs; 18 | private readonly string[]? _deleted; 19 | 20 | /// 21 | /// An Array reply containing all the successfully claimed messages in the same format as XRANGE 22 | /// 包含所有成功声明消息的数组,其格式与XRANGE相同 23 | /// 24 | public StreamValue[]? Messages => this._fvs; 25 | 26 | /// 27 | /// An Array reply containing message IDs that no longer exist in the stream, and were deleted from the PEL in which they were found 28 | /// Available since: 7.0.0 29 | /// 从PEL中清除的已删除条目ID 30 | /// 支持此返回值的Redis版本, 7.0.0+, 低版本永远为null 31 | /// 32 | public string[]? Deleted => this._deleted; 33 | 34 | internal XAutoClaimValue(string nextID, StreamValue[]? fvs, string[]? deleted) 35 | #else 36 | private readonly StreamValue[] _fvs; 37 | private readonly string[] _deleted; 38 | 39 | /// 40 | /// An Array reply containing all the successfully claimed messages in the same format as XRANGE 41 | /// 包含所有成功声明消息的数组,其格式与XRANGE相同 42 | /// 43 | public StreamValue[] Messages => this._fvs; 44 | 45 | /// 46 | /// An Array reply containing message IDs that no longer exist in the stream, and were deleted from the PEL in which they were found 47 | /// Available since: 7.0.0 48 | /// 从PEL中清除的已删除条目ID 49 | /// 支持此返回值的Redis版本, 7.0.0+, 低版本永远为null 50 | /// 51 | public string[] Deleted => this._deleted; 52 | 53 | internal XAutoClaimValue(string nextID, StreamValue[] fvs, string[] deleted) 54 | #endif 55 | { 56 | this._nextID = nextID; 57 | this._fvs = fvs; 58 | this._deleted = deleted; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/XInfoConsumersValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | #if NET5_0_OR_GREATER 3 | #pragma warning disable IDE0083 4 | #endif 5 | using SharpRedis.Extensions; 6 | using System; 7 | using System.Text; 8 | 9 | namespace SharpRedis 10 | { 11 | public sealed class XInfoConsumersValue : BaseXInfoConsumersValue 12 | { 13 | /// 14 | /// The number of entries in the PEL: pending messages for the consumer, which are messages that were delivered but are yet to be acknowledged 15 | /// 获得待处理的消息数量 16 | /// 17 | public long Pending { get; private set; } 18 | 19 | /// 20 | /// The number of milliseconds that have passed since the consumer's last attempted interaction 21 | /// 自消费者最后一次尝试交互之后的毫秒数 22 | /// 23 | public long Idle { get; private set; } 24 | 25 | /// 26 | /// The number of milliseconds that have passed since the consumer's last successful interaction 27 | /// Available since: 7.2.0. Redis below this version remains -1 28 | /// 自消费者最后一次成功交互之后的毫秒数, Redis 7.2.0+才有, 否则一直为-1 29 | /// 30 | public long Inactive { get; private set; } = -1; 31 | 32 | internal XInfoConsumersValue(object data, Encoding encoding) 33 | : base(data, encoding) 34 | { 35 | if (data is object[] array) 36 | { 37 | for (uint i = 0; i < array.Length; i += 2) 38 | { 39 | var key = ConvertExtensions.To(array[i], ResultType.String, encoding); 40 | switch (key) 41 | { 42 | case "pending": 43 | this.Pending = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 44 | continue; 45 | case "idle": 46 | this.Idle = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 47 | continue; 48 | case "inactive": 49 | this.Inactive = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 50 | continue; 51 | default: continue; 52 | } 53 | } 54 | } 55 | } 56 | 57 | public override string ToString() 58 | { 59 | return $"Name: {this.Name}, Pending: {this.Pending}, Idle: {this.Idle}, Inactive: {this.Inactive}"; 60 | } 61 | } 62 | 63 | public abstract class BaseXInfoConsumersValue 64 | { 65 | /// 66 | /// The consumer's name 67 | /// 获得消费者名称 68 | /// 69 | public string Name { get; private protected set; } 70 | 71 | private protected BaseXInfoConsumersValue(object data, Encoding encoding) 72 | { 73 | if (!(data is object[] array) || array.Length % 2 != 0) 74 | { 75 | throw new FormatException($"The data is not a valid XInfoConsumersValue, The actual type is {data.GetType().FullName}"); 76 | } 77 | 78 | for (uint i = 0; i < array.Length; i += 2) 79 | { 80 | var key = ConvertExtensions.To(array[i], ResultType.String, encoding); 81 | switch (key) 82 | { 83 | case "name": 84 | this.Name = ConvertExtensions.To(array[i + 1], ResultType.String, encoding) 85 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 86 | ! 87 | #endif 88 | ; 89 | goto End; 90 | default: continue; 91 | } 92 | } 93 | 94 | End: 95 | 96 | if (this.Name is null) throw new FormatException("Unrecognized name format"); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/XInfoGroupsValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | #if NET5_0_OR_GREATER 3 | #pragma warning disable IDE0083 4 | #endif 5 | using SharpRedis.Extensions; 6 | using System; 7 | using System.Text; 8 | 9 | namespace SharpRedis 10 | { 11 | public sealed class XInfoGroupsValue : BaseXInfoGroupsValue 12 | { 13 | /// 14 | /// The number of consumers in the group 15 | /// 消费组中的消费者数量 16 | /// 17 | public long Consumers { get; private set; } 18 | 19 | /// 20 | /// The length of the group's pending entries list (PEL), which are messages that were delivered but are yet to be acknowledged 21 | /// 消费组中待处理消息数量(未确认的消息数量) 22 | /// 23 | public long Pending { get; private set; } 24 | 25 | internal XInfoGroupsValue(object data, Encoding encoding) 26 | : base(data, encoding) 27 | { 28 | if (data is object[] array) 29 | { 30 | for (uint i = 0; i < array.Length; i += 2) 31 | { 32 | var key = ConvertExtensions.To(array[i], ResultType.String, encoding); 33 | switch (key) 34 | { 35 | case "pending": 36 | this.Pending = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 37 | continue; 38 | case "consumers": 39 | this.Consumers = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 40 | continue; 41 | default: continue; 42 | } 43 | } 44 | } 45 | } 46 | } 47 | 48 | public abstract class BaseXInfoGroupsValue 49 | { 50 | /// 51 | /// The consumer group's name 52 | /// 消费组名称 53 | /// 54 | public string Name { get; private protected set; } 55 | 56 | /// 57 | /// The ID of the last entry delivered to the group's consumers 58 | /// 最后交付给该组中消费者的条目的 ID 59 | /// 60 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 61 | public string? LastDeliveredID { get; private protected set; } 62 | #else 63 | public string LastDeliveredID { get; private protected set; } 64 | #endif 65 | 66 | /// 67 | /// The logical "read counter" of the last entry delivered to the group's consumers 68 | /// Available since: 7.0.0. Redis below this version remains -1 69 | /// 传递给组消费者的最后一个条目的逻辑“读计数器”. Redis 7.0.0+才有该返回值, 否则一直为-1 70 | /// 71 | public long EntriesRead { get; private protected set; } = -1; 72 | 73 | /// 74 | /// The number of entries in the stream that are still waiting to be delivered to the group's consumers, or a NULL when that number can't be determined 75 | /// Available since: 7.0.0 76 | /// Stream中仍在等待传递给组消费者的条目数,或者当无法确定该数字时为 NULL. Redis 7.0.0+才有该返回值 77 | /// 78 | public long? Lag { get; private protected set; } 79 | 80 | private protected BaseXInfoGroupsValue(object data, Encoding encoding) 81 | { 82 | if (!(data is object[] array) || array.Length % 2 != 0) 83 | { 84 | throw new FormatException($"The data is not a valid XInfoGroupsValue, The actual type is {data.GetType().FullName}"); 85 | } 86 | 87 | for (uint i = 0; i < array.Length; i += 2) 88 | { 89 | var key = ConvertExtensions.To(array[i], ResultType.String, encoding); 90 | switch (key) 91 | { 92 | case "name": 93 | this.Name = ConvertExtensions.To(array[i + 1], ResultType.String, encoding) 94 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 95 | ! 96 | #endif 97 | ; 98 | continue; 99 | case "entries-read": 100 | this.EntriesRead = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 101 | continue; 102 | case "lag": 103 | var lag = ConvertExtensions.To(array[i + 1], ResultType.Number, encoding); 104 | if (lag != null && lag.HasValue) this.Lag = lag; 105 | continue; 106 | case "last-delivered-id": 107 | this.LastDeliveredID = ConvertExtensions.To(array[i + 1], ResultType.String, encoding) 108 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 109 | ! 110 | #endif 111 | ; 112 | continue; 113 | default: continue; 114 | } 115 | } 116 | 117 | if (this.Name is null) throw new FormatException("Unrecognized name format"); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/SharpRedis/Values/XInfoStreamValue.cs: -------------------------------------------------------------------------------- 1 | #pragma warning disable IDE0130 2 | #if NET5_0_OR_GREATER 3 | #pragma warning disable IDE0083 4 | #endif 5 | using SharpRedis.Extensions; 6 | using System; 7 | using System.Text; 8 | 9 | namespace SharpRedis 10 | { 11 | public sealed class XInfoStreamValue : BaseXInfoStreamValue 12 | where TValue : class 13 | { 14 | /// 15 | /// The number of consumer groups defined for the stream 16 | /// 当前Stream上存在的消费者组(consumer group)数量 17 | /// 18 | public long Groups { get; private set; } 19 | 20 | /// 21 | /// The ID and field-value tuples of the first entry in the stream 22 | /// 当前Stream中第一个内容条目的ID及其数据 23 | /// 24 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 25 | public StreamValue? FirstEntry { get; private set; } 26 | #else 27 | public StreamValue FirstEntry { get; private set; } 28 | #endif 29 | 30 | /// 31 | /// The ID and field-value tuples of the last entry in the stream 32 | /// 当前Stream中最后一个内容条目的ID及其数据 33 | /// 34 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 35 | public StreamValue? LastEntry { get; private set; } 36 | #else 37 | public StreamValue LastEntry { get; private set; } 38 | #endif 39 | 40 | internal XInfoStreamValue(object data, Encoding encoding, ResultType valueType) 41 | : base(data, encoding, valueType) 42 | { 43 | if (data is object[] array) 44 | { 45 | for (uint i = 0; i < array.Length; i += 2) 46 | { 47 | var key = ConvertExtensions.To(array[i], ResultType.String, encoding); 48 | switch (key) 49 | { 50 | case "groups": 51 | this.Groups = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 52 | continue; 53 | case "first-entry": 54 | this.FirstEntry = ConvertExtensions.To>(array[i + 1], ResultType.Stream | valueType, encoding); 55 | continue; 56 | case "last-entry": 57 | this.LastEntry = ConvertExtensions.To>(array[i + 1], ResultType.Stream | valueType, encoding); 58 | continue; 59 | default: continue; 60 | } 61 | } 62 | } 63 | } 64 | } 65 | 66 | public abstract class BaseXInfoStreamValue 67 | { 68 | /// 69 | /// The number of entries in the stream 70 | /// Stream中的内容条目数量 71 | /// 72 | public long Length { get; private protected set; } 73 | 74 | /// 75 | /// The number of keys in the underlying radix data structure 76 | /// 内部 Radix 树的节点数量(键数)。Redis 使用 Radix 树存储流中的条目。这个值表示用于存储流的 Radix 树中的键的数量 77 | /// 78 | public long RadixTreeKeys { get; private protected set; } 79 | 80 | /// 81 | /// The number of nodes in the underlying radix data structure 82 | /// 内部 Radix 树的总节点数量。Redis 使用 Radix 树来高效地存储和查找条目,这个值表示 Radix 树中总共的节点数(包括内部节点和叶节点) 83 | /// 84 | public long RadixTreeNodes { get; private protected set; } 85 | 86 | /// 87 | /// The ID of the least-recently entry that was added to the stream 88 | /// 当前Stream中最后生成的内容条目的ID。每次使用 XADD 向流中添加内容条目时,Redis 会生成一个唯一的ID,这个字段表示流中最新的那个内容条目的ID 89 | /// 90 | public string LastGeneratedID { get; private protected set; } 91 | 92 | /// 93 | /// The maximal entry ID that was deleted from the stream 94 | /// 当前Stream中已删除内容条目的最大ID。这个字段表示Stream中所有已删除内容条目中ID的最大值。如果没有删除条目,通常为0-0 95 | /// 96 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 97 | public string? MaxDeletedEntryID { get; private protected set; } 98 | #else 99 | public string MaxDeletedEntryID { get; private protected set; } 100 | #endif 101 | 102 | /// 103 | /// The count of all entries added to the stream during its lifetime 104 | /// 自Stream创建以来添加的总内容条目数。包括当前存在的和已被删除的内容条目 105 | /// 106 | public long EntriesAdded { get; private protected set; } 107 | 108 | /// 109 | /// The minimum entry ID currently present in the stream (that is, the "first entry of record" ID) 110 | /// Available since: 5.0.0 111 | /// 当前Stream中存在的最小内容条目ID(即“Stream中的第一个条目”ID) 112 | /// Redis 7.0.0+以后才会返回, 否则一直为null 113 | /// 114 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 115 | public string? RecordedFirstEntryID { get; private protected set; } 116 | #else 117 | public string RecordedFirstEntryID { get; private protected set; } 118 | #endif 119 | 120 | private protected BaseXInfoStreamValue(object data, Encoding encoding, ResultType valueType) 121 | { 122 | if (!(data is object[] array) || array.Length % 2 != 0) 123 | { 124 | throw new FormatException($"The data is not a valid XInfoStreamValue, The actual type is {data.GetType().FullName}"); 125 | } 126 | 127 | for (uint i = 0; i < array.Length; i += 2) 128 | { 129 | var key = ConvertExtensions.To(array[i], ResultType.String, encoding); 130 | switch (key) 131 | { 132 | case "length": 133 | this.Length = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 134 | continue; 135 | case "radix-tree-keys": 136 | this.RadixTreeKeys = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 137 | continue; 138 | case "radix-tree-nodes": 139 | this.RadixTreeNodes = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 140 | continue; 141 | case "last-generated-id": 142 | this.LastGeneratedID = ConvertExtensions.To(array[i + 1], ResultType.String, encoding) 143 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 144 | ! 145 | #endif 146 | ; 147 | continue; 148 | case "max-deleted-entry-id": 149 | this.MaxDeletedEntryID = ConvertExtensions.To(array[i + 1], ResultType.String, encoding) 150 | #if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER 151 | ! 152 | #endif 153 | ; 154 | continue; 155 | case "entries-added": 156 | this.EntriesAdded = ConvertExtensions.To(array[i + 1], ResultType.Int64, encoding); 157 | continue; 158 | case "recorded-first-entry-id": 159 | this.RecordedFirstEntryID = ConvertExtensions.To(array[i + 1], ResultType.String, encoding); 160 | continue; 161 | default: continue; 162 | } 163 | } 164 | 165 | if (this.LastGeneratedID is null) 166 | { 167 | throw new FormatException("Unrecognized last-generated-id format"); 168 | } 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /test/NET8_Test/NET8_Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | false 9 | true 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test/NET8_Test/RedisProvider.cs: -------------------------------------------------------------------------------- 1 | using SharpRedis; 2 | 3 | namespace NET8_Test; 4 | 5 | public class RedisProvider : IEnumerable 6 | { 7 | private static readonly RedisItem _standalone = new(Redis.UseStandalone(option => 8 | { 9 | option.Password = "123456"; 10 | option.RespVersion = 3; 11 | })); 12 | 13 | private static readonly RedisItem _masterSlave = new(Redis.UseMasterSlave(2, (m, s) => 14 | { 15 | m.Password = "123456"; 16 | m.Port = 2352; 17 | 18 | s[0].Password = "123456"; 19 | s[0].Port = 2353; 20 | 21 | s[1].Password = "123456789"; 22 | s[1].Port = 2354; 23 | })); 24 | 25 | public IEnumerator GetEnumerator() 26 | { 27 | yield return [RedisProvider._standalone]; 28 | yield return [RedisProvider._masterSlave]; 29 | } 30 | 31 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 32 | => GetEnumerator(); 33 | } 34 | 35 | public class RedisItem(Redis redis) 36 | { 37 | private readonly Redis _redis = redis; 38 | 39 | public static implicit operator Redis(RedisItem item) => item._redis; 40 | } 41 | -------------------------------------------------------------------------------- /wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/q7164518/SharpRedis/77755066436b6e8cfb08d469a1256e1a59f998f3/wechat.png --------------------------------------------------------------------------------