diff --git a/Directory.Packages.props b/Directory.Packages.props
index cd2867a..e3bab0b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -28,6 +28,7 @@
+
diff --git a/Source/Starfish.Client/FodyWeavers.xml b/Source/Starfish.Client/FodyWeavers.xml
deleted file mode 100644
index 1715698..0000000
--- a/Source/Starfish.Client/FodyWeavers.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/Source/Starfish.Client/FodyWeavers.xsd b/Source/Starfish.Client/FodyWeavers.xsd
deleted file mode 100644
index ffa6fc4..0000000
--- a/Source/Starfish.Client/FodyWeavers.xsd
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
-
-
-
-
- A comma-separated list of error codes that can be safely ignored in assembly verification.
-
-
-
-
- 'false' to turn off automatic generation of the XML Schema file.
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/Starfish.Client/Starfish.Client.csproj b/Source/Starfish.Client/Starfish.Client.csproj
index be6db83..09c4499 100644
--- a/Source/Starfish.Client/Starfish.Client.csproj
+++ b/Source/Starfish.Client/Starfish.Client.csproj
@@ -3,8 +3,8 @@
- disable
- enable
+ true
+ A lightweight powerful distributed configuration server for .NET application.
@@ -42,9 +42,4 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/Source/Starfish.Common/RandomUtility.cs b/Source/Starfish.Common/RandomUtility.cs
index 5c9128b..d213c77 100644
--- a/Source/Starfish.Common/RandomUtility.cs
+++ b/Source/Starfish.Common/RandomUtility.cs
@@ -3,7 +3,7 @@
namespace Nerosoft.Starfish.Common;
///
-/// 随机数工具类
+/// A utility class for generating random numbers or strings.
///
internal class RandomUtility : Random
{
@@ -11,41 +11,14 @@ internal class RandomUtility : Random
private readonly byte[] _uint32Buffer = new byte[4];
///
- /// 创建随机数键值
+ /// Generate a random string with a given size.
///
- /// 长度
+ ///
///
- public static byte[] CreateRandomKey(int length)
- {
- var bytes = new byte[length];
- _generator.GetBytes(bytes);
-
- return bytes;
- }
-
- ///
- /// 创建随机键值字符串
- ///
- /// 长度
- ///
- public static string CreateRandomKeyString(int length)
- {
- var bytes = new byte[length];
- _generator.GetBytes(bytes);
-
- return Convert.ToBase64String(bytes);
- }
-
- ///
- /// 创建唯一编号
- ///
- /// 长度
- ///
- public static string CreateUniqueId(int length = 32)
+ public static string GenerateUniqueId(int length = 32)
{
var bytes = new byte[Convert.ToInt32(length / 2)];
_generator.GetBytes(bytes);
- //转化为16进制
var hex = new StringBuilder(bytes.Length * 2);
foreach (var b in bytes)
{
@@ -55,21 +28,6 @@ public static string CreateUniqueId(int length = 32)
return hex.ToString();
}
- ///
- /// Initializes a new instance of the class.
- ///
- public RandomUtility()
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// seed (ignored)
- public RandomUtility(int ignoredSeed)
- {
- }
-
///
/// Returns a nonnegative random number.
///
@@ -158,4 +116,4 @@ public override void NextBytes(byte[] buffer)
throw new ArgumentNullException(nameof(buffer));
_generator.GetBytes(buffer);
}
-}
+}
\ No newline at end of file
diff --git a/Source/Starfish.Redis/README.md b/Source/Starfish.Redis/README.md
new file mode 100644
index 0000000..a15a20e
--- /dev/null
+++ b/Source/Starfish.Redis/README.md
@@ -0,0 +1,95 @@
+# Microsoft.Extensions.Configuration.Redis
+
+Redis configuration provider implementation for [Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration/).
+
+## Redis key/value
+
+The Redis configuration provider requires a hash key in Redis. The following example shows how to store json settings to Redis hash structure.
+
+> **Note:** The Redis configuration provider does not support nested values.
+
+**Origin json data**
+
+```json
+{
+ "Settings": {
+ "Server": "localhost",
+ "Database": "master",
+ "Ports": [ "1433", "1434", "1435" ]
+ }
+}
+```
+
+The Redis key/value pair structure is shown below.
+
+**Redis key:**
+`appsettings`
+
+**Redis value:**
+```text
+Settings:Server = localhost
+Settings:Database = master
+Settings:Ports:0 = 1433
+Settings:Ports:1 = 1434
+Settings:Ports:2 = 1435
+```
+
+## How to?
+The following example shows how to read application settings from the Redis.
+
+### Install NuGet package
+
+NuGet package: [Starfish.Redis](https://www.nuget.org/packages/Starfish.Redis/)
+
+```bash
+Install-Package Starfish.Redis
+```
+
+```powershell
+dotnet add package Starfish.Redis
+```
+
+```xml
+
+```
+
+### Add Redis configuration provider
+
+```cs
+using System;
+using Microsoft.Extensions.Configuration;
+
+class Program
+{
+ static void Main()
+ {
+ IConfiguration config = new ConfigurationBuilder()
+ .AddRedis("127.0.0.1:6379,ssl=False,allowAdmin=True,abortConnect=False,defaultDatabase=0,connectTimeout=500,connectRetry=3", "appsettings")
+ .Build();
+
+ // Get a configuration section
+ IConfigurationSection section = config.GetSection("Settings");
+
+ // Read simple values
+ Console.WriteLine($"Server: {section["Server"]}");
+ Console.WriteLine($"Database: {section["Database"]}");
+
+ // Read a collection
+ Console.WriteLine("Ports: ");
+ IConfigurationSection ports = section.GetSection("Ports");
+
+ foreach (IConfigurationSection child in ports.GetChildren())
+ {
+ Console.WriteLine(child.Value);
+ }
+ }
+}
+```
+
+## Enable Redis Keyspace Notifications
+
+The Redis configuration provider uses Redis keyspace notifications to invalidate the cache when the configuration changes. The Redis keyspace notifications are disabled by default. To enable the Redis keyspace notifications, set the `notify-keyspace-events` configuration option in the Redis configuration file to `AKE`.
+
+1. Open terminal and run `redis-cli`.
+2. Check the current configuration value use `CONFIG GET notify-keyspace-events`.
+3. Set the `notify-keyspace-events` configuration option to `AKE` use `CONFIG SET notify-keyspace-events AKE`.
\ No newline at end of file
diff --git a/Source/Starfish.Redis/RedisConfigurationClient.cs b/Source/Starfish.Redis/RedisConfigurationClient.cs
new file mode 100644
index 0000000..d5202f9
--- /dev/null
+++ b/Source/Starfish.Redis/RedisConfigurationClient.cs
@@ -0,0 +1,116 @@
+using Microsoft.Extensions.Primitives;
+using StackExchange.Redis;
+
+namespace Microsoft.Extensions.Configuration.Redis;
+
+internal class RedisConfigurationClient : IAsyncDisposable
+{
+ private readonly int _database;
+ private readonly string _key;
+ private readonly bool _keyspaceEnabled;
+
+ private readonly SemaphoreSlim _semaphoreSlim = new(0, 1);
+
+ private Timer _timer;
+ private ISubscriber _subscriber;
+
+ public RedisConfigurationClient(string connectionString, int database, string key, bool keyspaceEnabled)
+ {
+ _database = database;
+ _key = key;
+ _keyspaceEnabled = keyspaceEnabled;
+ Connect(connectionString);
+ }
+
+ private ConnectionMultiplexer Connection { get; set; }
+
+ private CancellationTokenSource CancellationSource { get; set; }
+
+ public async Task> LoadAsync()
+ {
+ await _semaphoreSlim.WaitAsync();
+
+ if (Connection == null)
+ {
+ return [];
+ }
+
+ return await Connection.GetDatabase(_database)
+ .HashGetAllAsync(_key)
+ .ContinueWith(task =>
+ {
+ return task.Result.ToDictionary(x => x.Name.ToString(), x => ReadRedisValue(x.Value));
+ });
+ }
+
+ private async void Connect(string connectionString)
+ {
+ try
+ {
+ Connection = await ConnectionMultiplexer.ConnectAsync(connectionString);
+
+ if (_keyspaceEnabled)
+ {
+ var channel = RedisChannel.Pattern($"__keyspace@{_database}__:{_key}");
+
+ _subscriber = Connection.GetSubscriber();
+ await _subscriber.SubscribeAsync(channel, (_, _) =>
+ {
+ CancellationSource?.Cancel();
+ });
+ }
+ else
+ {
+ _timer = new Timer(_ => CancellationSource?.Cancel(), null, TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(30));
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ finally
+ {
+ _semaphoreSlim.Release();
+ }
+ }
+
+ private static string ReadRedisValue(RedisValue value)
+ {
+ if (value.IsNull)
+ {
+ return null;
+ }
+
+ return value.IsNullOrEmpty ? string.Empty : value.ToString();
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ if (Connection != null)
+ {
+ await Connection.CloseAsync();
+ await Connection.DisposeAsync();
+ }
+
+ if (_timer != null)
+ {
+ await _timer.DisposeAsync();
+ }
+
+ if (_subscriber != null)
+ {
+ await _subscriber.UnsubscribeAllAsync();
+ _subscriber = null;
+ }
+
+ CancellationSource?.Dispose();
+ }
+
+ public IChangeToken Watch()
+ {
+ CancellationSource?.Dispose();
+ CancellationSource = new CancellationTokenSource();
+ var cancellationToken = new CancellationChangeToken(CancellationSource.Token);
+ return cancellationToken;
+ }
+}
\ No newline at end of file
diff --git a/Source/Starfish.Redis/RedisConfigurationExtensions.cs b/Source/Starfish.Redis/RedisConfigurationExtensions.cs
new file mode 100644
index 0000000..5e0c13c
--- /dev/null
+++ b/Source/Starfish.Redis/RedisConfigurationExtensions.cs
@@ -0,0 +1,40 @@
+namespace Microsoft.Extensions.Configuration.Redis;
+
+public static class RedisConfigurationExtensions
+{
+ ///
+ /// Adds a Redis configuration source to .
+ ///
+ /// The to add to.
+ /// The Redis connection string.
+ /// The Redis key to read configuration data.
+ /// The Redis database ID.
+ /// A value indicating whether keyspace events are enabled. Use CONFIG GET notify-keyspace-events
to check.
+ /// Whether the configuration should be reloaded if the value changes.
+ /// The .
+ public static IConfigurationBuilder AddRedis(this IConfigurationBuilder builder, string connectionString, string key, int database = -1, bool keyspaceEnabled = true, bool reloadOnChange = true)
+ {
+ ArgumentNullException.ThrowIfNull(builder, nameof(builder));
+ ArgumentNullException.ThrowIfNull(connectionString, nameof(connectionString));
+ ArgumentNullException.ThrowIfNull(key, nameof(key));
+ return builder.AddRedis(source =>
+ {
+ source.ConnectionString = connectionString;
+ source.Key = key;
+ source.Database = database;
+ source.KeyspaceEnabled = keyspaceEnabled;
+ source.ReloadOnChange = reloadOnChange;
+ });
+ }
+
+ ///
+ /// Adds a Redis configuration source to .
+ ///
+ /// The to add to.
+ /// Configures the source.
+ /// The .
+ public static IConfigurationBuilder AddRedis(this IConfigurationBuilder builder, Action configureSource)
+ {
+ return builder.Add(configureSource);
+ }
+}
\ No newline at end of file
diff --git a/Source/Starfish.Redis/RedisConfigurationProvider.cs b/Source/Starfish.Redis/RedisConfigurationProvider.cs
new file mode 100644
index 0000000..246330b
--- /dev/null
+++ b/Source/Starfish.Redis/RedisConfigurationProvider.cs
@@ -0,0 +1,36 @@
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.Extensions.Configuration.Redis;
+
+///
+///
+///
+public sealed class RedisConfigurationProvider : ConfigurationProvider, IAsyncDisposable
+{
+ private readonly RedisConfigurationClient _client;
+
+ private readonly IDisposable _changeToken;
+
+ public RedisConfigurationProvider(RedisConfigurationSource source)
+ {
+ _client = new RedisConfigurationClient(source.ConnectionString, source.Database, source.Key, source.KeyspaceEnabled);
+
+ if (source.ReloadOnChange)
+ {
+ _changeToken = ChangeToken.OnChange(_client.Watch, Load);
+ }
+ }
+
+ public override void Load()
+ {
+ Data = _client.LoadAsync()
+ .GetAwaiter()
+ .GetResult();
+ }
+
+ public async ValueTask DisposeAsync()
+ {
+ _changeToken?.Dispose();
+ await _client.DisposeAsync();
+ }
+}
\ No newline at end of file
diff --git a/Source/Starfish.Redis/RedisConfigurationSource.cs b/Source/Starfish.Redis/RedisConfigurationSource.cs
new file mode 100644
index 0000000..cd9d5d5
--- /dev/null
+++ b/Source/Starfish.Redis/RedisConfigurationSource.cs
@@ -0,0 +1,41 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.Extensions.Configuration.Redis;
+
+public sealed class RedisConfigurationSource : IConfigurationSource
+{
+ ///
+ /// The Redis connection string.
+ ///
+ [DisallowNull]
+ public string ConnectionString { get; set; }
+
+ ///
+ /// Gets or sets the Redis database ID.
+ ///
+ public int Database { get; set; } = -1;
+
+ ///
+ /// Gets or sets the Redis key this source will read from.
+ ///
+ ///
+ /// The key is expected to be a hash.
+ ///
+ public string Key { get; set; } = "appsettings";
+
+ ///
+ /// Determines whether the source will be loaded if the underlying file changes.
+ ///
+ public bool ReloadOnChange { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether keyspace events are enabled.
+ ///
+ public bool KeyspaceEnabled { get; set; }
+
+ ///
+ public IConfigurationProvider Build(IConfigurationBuilder builder)
+ {
+ return new RedisConfigurationProvider(this);
+ }
+}
\ No newline at end of file
diff --git a/Source/Starfish.Redis/Starfish.Redis.csproj b/Source/Starfish.Redis/Starfish.Redis.csproj
new file mode 100644
index 0000000..d5b7dd7
--- /dev/null
+++ b/Source/Starfish.Redis/Starfish.Redis.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+ true
+ Microsoft.Extensions.Configuration.Redis
+ Redis configuration provider implementation for Microsoft.Extensions.Configuration. It's enables you to read your application's settings from a specified Redis hash key.
+
+
+
+
+
+
+
+
diff --git a/Source/common.props b/Source/common.props
index b773683..75eb231 100644
--- a/Source/common.props
+++ b/Source/common.props
@@ -2,17 +2,15 @@
net6.0;net7.0;net8.0
Nerosoft.$(MSBuildProjectName.Replace(" ", "_"))
- 1.0.0
+ 1.0.1
damon
- Nerosoft Co., Ltd.
+ Nerosoft Ltd.
Starfish
© 2018-2023 Nerosoft. All Rights Reserved.
enable
disable
latest
- A lightweight powerful distributed configuration server for .NET application.
https://github.com/NerosoftDev/Starfish/
- README.md
https://github.com/NerosoftDev/Starfish/
.net, asp.net, core, configuration, config, appsettings, configuration-management, configuration-service, configuration-server, configuration-center
Debug;Release;Product
diff --git a/Starfish.sln b/Starfish.sln
index 1aeb8f8..a9e8d80 100644
--- a/Starfish.sln
+++ b/Starfish.sln
@@ -40,9 +40,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Starfish.Common", "Source\S
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Starfish.Sample.Webapi", "Samples\Starfish.Sample.Webapi\Starfish.Sample.Webapi.csproj", "{6C29FD68-4D15-4D8B-A6DF-42216074CD1B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Starfish.Sample.Blazor", "Samples\Starfish.Sample.Blazor\Starfish.Sample.Blazor.csproj", "{EA58AD06-DA8C-469A-81C3-DDF9C75F8872}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Starfish.Sample.Blazor", "Samples\Starfish.Sample.Blazor\Starfish.Sample.Blazor.csproj", "{EA58AD06-DA8C-469A-81C3-DDF9C75F8872}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Starfish.Sample.MauiApp", "Samples\Starfish.Sample.MauiApp\Starfish.Sample.MauiApp.csproj", "{A0B1FBB3-F4E5-4462-B855-DB8044D7D03E}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Starfish.Sample.MauiApp", "Samples\Starfish.Sample.MauiApp\Starfish.Sample.MauiApp.csproj", "{A0B1FBB3-F4E5-4462-B855-DB8044D7D03E}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Starfish.Redis", "Source\Starfish.Redis\Starfish.Redis.csproj", "{4EF74AB0-401F-4FEF-BF5C-25CF1C8F5ECE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -92,6 +94,10 @@ Global
{A0B1FBB3-F4E5-4462-B855-DB8044D7D03E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0B1FBB3-F4E5-4462-B855-DB8044D7D03E}.Release|Any CPU.Build.0 = Release|Any CPU
{A0B1FBB3-F4E5-4462-B855-DB8044D7D03E}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {4EF74AB0-401F-4FEF-BF5C-25CF1C8F5ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4EF74AB0-401F-4FEF-BF5C-25CF1C8F5ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4EF74AB0-401F-4FEF-BF5C-25CF1C8F5ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4EF74AB0-401F-4FEF-BF5C-25CF1C8F5ECE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -107,6 +113,7 @@ Global
{6C29FD68-4D15-4D8B-A6DF-42216074CD1B} = {5FFE10B6-8023-4475-9A43-D4EF7AC99009}
{EA58AD06-DA8C-469A-81C3-DDF9C75F8872} = {5FFE10B6-8023-4475-9A43-D4EF7AC99009}
{A0B1FBB3-F4E5-4462-B855-DB8044D7D03E} = {5FFE10B6-8023-4475-9A43-D4EF7AC99009}
+ {4EF74AB0-401F-4FEF-BF5C-25CF1C8F5ECE} = {31CE15C0-0BE0-4444-AC72-9E3C2B87BD7C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BFF17094-761E-48C4-96A9-16248CBF46BA}