diff --git a/Akka.Persistence.Redis.sln b/Akka.Persistence.Redis.sln
index c244b8b..015da47 100644
--- a/Akka.Persistence.Redis.sln
+++ b/Akka.Persistence.Redis.sln
@@ -32,6 +32,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Akka.Persistence.Redis.Benchmark.DockerTests", "src\benchmarks\Akka.Persistence.Redis.Benchmark.DockerTests\Akka.Persistence.Redis.Benchmark.DockerTests.csproj", "{BFBB3933-E8FC-4574-8D60-E6FA6BEABAE9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Redis.Hosting", "src\Akka.Persistence.Redis.Hosting\Akka.Persistence.Redis.Hosting.csproj", "{80BD65FD-3AB6-4AF1-B0CD-573C9D50DB1E}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build-system", "build-system", "{D118A7A7-A89D-450D-8218-E2AE580DEF5D}"
ProjectSection(SolutionItems) = preProject
build-system\azure-pipeline.template.yaml = build-system\azure-pipeline.template.yaml
@@ -70,6 +72,10 @@ Global
{BFBB3933-E8FC-4574-8D60-E6FA6BEABAE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFBB3933-E8FC-4574-8D60-E6FA6BEABAE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFBB3933-E8FC-4574-8D60-E6FA6BEABAE9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {80BD65FD-3AB6-4AF1-B0CD-573C9D50DB1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {80BD65FD-3AB6-4AF1-B0CD-573C9D50DB1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {80BD65FD-3AB6-4AF1-B0CD-573C9D50DB1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {80BD65FD-3AB6-4AF1-B0CD-573C9D50DB1E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Akka.Persistence.Redis.Hosting/Akka.Persistence.Redis.Hosting.csproj b/src/Akka.Persistence.Redis.Hosting/Akka.Persistence.Redis.Hosting.csproj
new file mode 100644
index 0000000..27d8a46
--- /dev/null
+++ b/src/Akka.Persistence.Redis.Hosting/Akka.Persistence.Redis.Hosting.csproj
@@ -0,0 +1,19 @@
+
+
+
+ $(NetStandardVersion)
+ README.md
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Akka.Persistence.Redis.Hosting/AkkaPersistenceRedisHostingExtensions.cs b/src/Akka.Persistence.Redis.Hosting/AkkaPersistenceRedisHostingExtensions.cs
new file mode 100644
index 0000000..90ad7b3
--- /dev/null
+++ b/src/Akka.Persistence.Redis.Hosting/AkkaPersistenceRedisHostingExtensions.cs
@@ -0,0 +1,216 @@
+using System;
+using System.Text;
+using Akka.Actor;
+using Akka.Configuration;
+using Akka.Hosting;
+using Akka.Persistence.Hosting;
+
+#nullable enable
+namespace Akka.Persistence.Redis.Hosting;
+
+public static class AkkaPersistenceRedisHostingExtensions
+{
+ ///
+ /// Adds Akka.Persistence.Redis support to this .
+ ///
+ ///
+ /// The builder instance being configured.
+ ///
+ ///
+ /// Connection string as described here: https://stackexchange.github.io/StackExchange.Redis/Configuration#basic-configuration-strings.
+ ///
+ ///
+ ///
+ /// Determines which settings should be added by this method call.
+ ///
+ /// Default:
+ ///
+ ///
+ ///
+ /// Should the redis store table be initialized automatically.
+ ///
+ /// Default: false
+ ///
+ ///
+ ///
+ /// An used to configure an instance.
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// The configuration identifier for the plugins
+ ///
+ /// Default: "redis"
+ ///
+ ///
+ ///
+ /// A bool flag to set the plugin as the default persistence plugin for the
+ ///
+ /// Default: true
+ ///
+ ///
+ /// The same instance originally passed in.
+ ///
+ ///
+ /// Thrown when is set and is set to
+ ///
+ ///
+ public static AkkaConfigurationBuilder WithRedisPersistence(
+ this AkkaConfigurationBuilder builder,
+ string configurationString,
+ PersistenceMode mode = PersistenceMode.Both,
+ bool autoInitialize = true,
+ Action? journalBuilder = null,
+ string pluginIdentifier = "redis",
+ bool isDefaultPlugin = true)
+ {
+ if (mode == PersistenceMode.SnapshotStore && journalBuilder is { })
+ throw new Exception(
+ $"{nameof(journalBuilder)} can only be set when {nameof(mode)} is set to either {PersistenceMode.Both} or {PersistenceMode.Journal}");
+
+ var journalOpt = new RedisJournalOptions(isDefaultPlugin, pluginIdentifier)
+ {
+ ConfigurationString = configurationString,
+ AutoInitialize = autoInitialize,
+ };
+
+ var adapters = new AkkaPersistenceJournalBuilder(journalOpt.Identifier, builder);
+ journalBuilder?.Invoke(adapters);
+ journalOpt.Adapters = adapters;
+
+ var snapshotOpt = new RedisSnapshotOptions(isDefaultPlugin, pluginIdentifier)
+ {
+ ConfigurationString = configurationString,
+ AutoInitialize = autoInitialize,
+ };
+
+ return mode switch
+ {
+ PersistenceMode.Journal => builder.WithRedisPersistence(journalOpt, null),
+ PersistenceMode.SnapshotStore => builder.WithRedisPersistence(null, snapshotOpt),
+ PersistenceMode.Both => builder.WithRedisPersistence(journalOpt, snapshotOpt),
+ _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Invalid PersistenceMode defined.")
+ };
+ }
+
+ ///
+ /// Adds Akka.Persistence.Redis support to this . At least one of the
+ /// configurator delegate needs to be populated else this method will throw an exception.
+ ///
+ ///
+ /// The builder instance being configured.
+ ///
+ ///
+ ///
+ /// An that modifies an instance of ,
+ /// used to configure the journal plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// An that modifies an instance of ,
+ /// used to configure the snapshot store plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// A bool flag to set the plugin as the default persistence plugin for the
+ ///
+ /// Default: true
+ ///
+ ///
+ /// The same instance originally passed in.
+ ///
+ ///
+ /// Thrown when both and are null.
+ ///
+ public static AkkaConfigurationBuilder WithRedisPersistence(
+ this AkkaConfigurationBuilder builder,
+ Action? journalOptionConfigurator = null,
+ Action? snapshotOptionConfigurator = null,
+ bool isDefaultPlugin = true)
+ {
+ if (journalOptionConfigurator is null && snapshotOptionConfigurator is null)
+ throw new ArgumentException(
+ $"{nameof(journalOptionConfigurator)} and {nameof(snapshotOptionConfigurator)} could not both be null");
+
+ RedisJournalOptions? journalOptions = null;
+ if (journalOptionConfigurator is { })
+ {
+ journalOptions = new RedisJournalOptions(isDefaultPlugin);
+ journalOptionConfigurator(journalOptions);
+ }
+
+ RedisSnapshotOptions? snapshotOptions = null;
+ if (snapshotOptionConfigurator is { })
+ {
+ snapshotOptions = new RedisSnapshotOptions(isDefaultPlugin);
+ snapshotOptionConfigurator(snapshotOptions);
+ }
+
+ return builder.WithRedisPersistence(journalOptions, snapshotOptions);
+ }
+
+ ///
+ /// Adds Akka.Persistence.Redis support to this . At least one of the options
+ /// have to be populated else this method will throw an exception.
+ ///
+ ///
+ /// The builder instance being configured.
+ ///
+ ///
+ ///
+ /// An instance of , used to configure the journal plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ ///
+ /// An instance of , used to configure the snapshot store plugin
+ ///
+ /// Default: null
+ ///
+ ///
+ /// The same instance originally passed in.
+ ///
+ ///
+ /// Thrown when both and are null.
+ ///
+ public static AkkaConfigurationBuilder WithRedisPersistence(
+ this AkkaConfigurationBuilder builder,
+ RedisJournalOptions? journalOptions = null,
+ RedisSnapshotOptions? snapshotOptions = null)
+ {
+ if (journalOptions is null && snapshotOptions is null)
+ throw new ArgumentException($"{nameof(journalOptions)} and {nameof(snapshotOptions)} could not both be null");
+
+ return (journalOptions, snapshotOptions) switch
+ {
+ (null, null) =>
+ throw new ArgumentException(
+ $"{nameof(journalOptions)} and {nameof(snapshotOptions)} could not both be null"),
+
+ (_, null) =>
+ builder
+ .AddHocon(journalOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(journalOptions.DefaultConfig, HoconAddMode.Append)
+ .AddHocon(RedisPersistence.DefaultConfig(), HoconAddMode.Append),
+
+ (null, _) =>
+ builder
+ .AddHocon(snapshotOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(snapshotOptions.DefaultConfig, HoconAddMode.Append),
+
+ (_, _) =>
+ builder
+ .AddHocon(journalOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(snapshotOptions.ToConfig(), HoconAddMode.Prepend)
+ .AddHocon(journalOptions.DefaultConfig, HoconAddMode.Append)
+ .AddHocon(snapshotOptions.DefaultConfig, HoconAddMode.Append)
+ .AddHocon(RedisPersistence.DefaultConfig(), HoconAddMode.Append),
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.Redis.Hosting/README.md b/src/Akka.Persistence.Redis.Hosting/README.md
new file mode 100644
index 0000000..7dd3990
--- /dev/null
+++ b/src/Akka.Persistence.Redis.Hosting/README.md
@@ -0,0 +1,87 @@
+# Akka.Persistence.Redis.Hosting
+
+Akka.Hosting extension methods to add Akka.Persistence.Redis to an ActorSystem
+
+# Akka.Persistence.Redis Extension Methods
+
+## WithRedisPersistence() Method
+
+```csharp
+public static AkkaConfigurationBuilder WithRedisPersistence(
+ this AkkaConfigurationBuilder builder,
+ string configurationString,
+ PersistenceMode mode = PersistenceMode.Both,
+ bool autoInitialize = true,
+ Action? journalBuilder = null,
+ string pluginIdentifier = "Redis",
+ bool isDefaultPlugin = true);
+```
+
+```csharp
+public static AkkaConfigurationBuilder WithRedisPersistence(
+ this AkkaConfigurationBuilder builder,
+ Action? journalOptionConfigurator = null,
+ Action? snapshotOptionConfigurator = null,
+ bool isDefaultPlugin = true)
+```
+
+```csharp
+public static AkkaConfigurationBuilder WithRedisPersistence(
+ this AkkaConfigurationBuilder builder,
+ RedisJournalOptions? journalOptions = null,
+ RedisSnapshotOptions? snapshotOptions = null)
+```
+
+### Parameters
+
+* `configurationString` __string__
+
+ Connection string used for database access. Connection string as described here: https://stackexchange.github.io/StackExchange.Redis/Configuration#basic-configuration-strings.
+
+* `mode` __PersistenceMode__
+
+ Determines which settings should be added by this method call. __Default__: `PersistenceMode.Both`
+
+ * `PersistenceMode.Journal`: Only add the journal settings
+ * `PersistenceMode.SnapshotStore`: Only add the snapshot store settings
+ * `PersistenceMode.Both`: Add both journal and snapshot store settings
+
+* `autoInitialize` __bool__
+
+ Should the Redis store collection be initialized automatically. __Default__: `false`
+
+* `journalBuilder` __Action\__
+
+ An Action delegate used to configure an `AkkaPersistenceJournalBuilder` instance. Used to configure [Event Adapters](https://getakka.net/articles/persistence/event-adapters.html)
+
+* `journalConfigurator` __Action\__
+
+ An Action delegate to configure a `RedisJournalOptions` instance.
+
+* `snapshotConfigurator` __Action\__
+
+ An Action delegate to configure a `RedisSnapshotOptions` instance.
+
+* `journalOptions` __RedisJournalOptions__
+
+ An `RedisJournalOptions` instance to configure the Redis journal store.
+
+* `snapshotOptions` __RedisSnapshotOptions__
+
+ An `RedisSnapshotOptions` instance to configure the Redis snapshot store.
+
+## Example
+
+```csharp
+using var host = new HostBuilder()
+ .ConfigureServices((context, services) =>
+ {
+ services.AddAkka("redisDemo", (builder, provider) =>
+ {
+ builder
+ .WithRedisPersistence("your-redis-connection-string");
+ });
+ }).Build();
+
+await host.RunAsync();
+```
\ No newline at end of file
diff --git a/src/Akka.Persistence.Redis.Hosting/RedisJournalOptions.cs b/src/Akka.Persistence.Redis.Hosting/RedisJournalOptions.cs
new file mode 100644
index 0000000..577f33a
--- /dev/null
+++ b/src/Akka.Persistence.Redis.Hosting/RedisJournalOptions.cs
@@ -0,0 +1,60 @@
+using System.Text;
+using Akka.Configuration;
+using Akka.Hosting;
+using Akka.Persistence.Hosting;
+
+#nullable enable
+namespace Akka.Persistence.Redis.Hosting;
+
+public class RedisJournalOptions : JournalOptions
+{
+ private static readonly Config Default = RedisPersistence.DefaultConfig().GetConfig(RedisPersistence.JournalConfigPath);
+
+ public RedisJournalOptions() : this(true)
+ {
+ }
+
+ public RedisJournalOptions(bool isDefault, string identifier = "redis") : base(isDefault)
+ {
+ Identifier = identifier;
+ }
+
+ ///
+ /// Connection string, as described here: https://stackexchange.github.io/StackExchange.Redis/Configuration#basic-configuration-strings
+ ///
+ public string ConfigurationString { get; set; } = string.Empty;
+
+ ///
+ /// Redis journals key prefixes. Leave it for default or change it to appropriate value. WARNING: don't change it on production instances.
+ ///
+ public string? KeyPrefix { get; set; }
+
+ ///
+ /// Set the Redis default database to use. If you added defaultDatabase to the connection-strings, you have to set database to the value of defaultDatabase.
+ ///
+ public int? Database { get; set; }
+
+ ///
+ /// Determines redis database precedence when a user adds defaultDatabase to the connection-strings. For Redis Cluster, the defaultDatabase is 0
+ ///
+ public bool? UseDatabaseFromConnectionString { get; set; }
+
+ public override string Identifier { get; set; }
+ protected override Config InternalDefaultConfig { get; } = Default;
+
+ protected override StringBuilder Build(StringBuilder sb)
+ {
+ sb.AppendLine($"configuration-string = {ConfigurationString.ToHocon()}");
+
+ if (KeyPrefix is not null)
+ sb.AppendLine($"key-prefix = {KeyPrefix.ToHocon()}");
+
+ if (Database is not null)
+ sb.AppendLine($"database = {Database.ToHocon()}");
+
+ if (UseDatabaseFromConnectionString is not null)
+ sb.AppendLine($"use-database-number-from-connection-string = {UseDatabaseFromConnectionString.Value.ToHocon()}");
+
+ return base.Build(sb);
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.Redis.Hosting/RedisSnapshotOptions.cs b/src/Akka.Persistence.Redis.Hosting/RedisSnapshotOptions.cs
new file mode 100644
index 0000000..c47e352
--- /dev/null
+++ b/src/Akka.Persistence.Redis.Hosting/RedisSnapshotOptions.cs
@@ -0,0 +1,61 @@
+using System.Text;
+using Akka.Configuration;
+using Akka.Hosting;
+using Akka.Persistence.Hosting;
+
+#nullable enable
+namespace Akka.Persistence.Redis.Hosting;
+
+public class RedisSnapshotOptions : SnapshotOptions
+{
+ private static readonly Config Default = RedisPersistence.DefaultConfig()
+ .GetConfig(RedisPersistence.SnapshotConfigPath);
+
+ public RedisSnapshotOptions() : this(true)
+ {
+ }
+
+ public RedisSnapshotOptions(bool isDefault, string identifier = "redis") : base(isDefault)
+ {
+ Identifier = identifier;
+ }
+
+ ///
+ /// Connection string, as described here: https://stackexchange.github.io/StackExchange.Redis/Configuration#basic-configuration-strings
+ ///
+ public string ConfigurationString { get; set; } = string.Empty;
+
+ ///
+ /// Redis journals key prefixes. Leave it for default or change it to appropriate value. WARNING: don't change it on production instances.
+ ///
+ public string? KeyPrefix { get; set; }
+
+ ///
+ /// Set the Redis default database to use. If you added defaultDatabase to the connection-strings, you have to set database to the value of defaultDatabase.
+ ///
+ public int? Database { get; set; }
+
+ ///
+ /// Determines redis database precedence when a user adds defaultDatabase to the connection-strings. For Redis Cluster, the defaultDatabase is 0
+ ///
+ public bool? UseDatabaseFromConnectionString { get; set; }
+
+ public override string Identifier { get; set; }
+ protected override Config InternalDefaultConfig { get; } = Default;
+
+ protected override StringBuilder Build(StringBuilder sb)
+ {
+ sb.AppendLine($"configuration-string = {ConfigurationString.ToHocon()}");
+
+ if (KeyPrefix is not null)
+ sb.AppendLine($"key-prefix = {KeyPrefix.ToHocon()}");
+
+ if (Database is not null)
+ sb.AppendLine($"database = {Database.ToHocon()}");
+
+ if (UseDatabaseFromConnectionString is not null)
+ sb.AppendLine($"use-database-number-from-connection-string = {UseDatabaseFromConnectionString.Value.ToHocon()}");
+
+ return base.Build(sb);
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.Redis.Tests/Akka.Persistence.Redis.Tests.csproj b/src/Akka.Persistence.Redis.Tests/Akka.Persistence.Redis.Tests.csproj
index 80fd921..e95d95d 100644
--- a/src/Akka.Persistence.Redis.Tests/Akka.Persistence.Redis.Tests.csproj
+++ b/src/Akka.Persistence.Redis.Tests/Akka.Persistence.Redis.Tests.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/Akka.Persistence.Redis.Tests/Hosting/RedisJournalOptionsSpec.cs b/src/Akka.Persistence.Redis.Tests/Hosting/RedisJournalOptionsSpec.cs
new file mode 100644
index 0000000..0e12436
--- /dev/null
+++ b/src/Akka.Persistence.Redis.Tests/Hosting/RedisJournalOptionsSpec.cs
@@ -0,0 +1,136 @@
+using System;
+using System.IO;
+using System.Text;
+using Akka.Configuration;
+using Akka.Persistence.Redis.Hosting;
+using FluentAssertions;
+using FluentAssertions.Extensions;
+using Microsoft.Extensions.Configuration;
+using Xunit;
+
+namespace Akka.Persistence.Redis.Tests.Hosting
+{
+ public class RedisJournalOptionsSpec
+ {
+ [Fact(DisplayName = "RedisJournalOptions as default plugin should generate plugin setting")]
+ public void DefaultPluginJournalOptionsTest()
+ {
+ var options = new RedisJournalOptions(true);
+ var config = options.ToConfig();
+
+ config.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.redis");
+ config.HasPath("akka.persistence.journal.redis").Should().BeTrue();
+ }
+
+ [Fact(DisplayName = "Empty RedisJournalOptions should equal empty config with default fallback")]
+ public void DefaultJournalOptionsTest()
+ {
+ var options = new RedisJournalOptions(false);
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(RedisPersistence.DefaultConfig());
+
+ emptyRootConfig.GetString("akka.persistence.journal.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.journal.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.journal.redis");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.journal.redis");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("configuration-string").Should().Be(baseConfig.GetString("configuration-string"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("key-prefix").Should().Be(baseConfig.GetString("key-prefix"));
+ config.GetInt("database").Should().Be(baseConfig.GetInt("database"));
+ config.GetBoolean("use-database-number-from-connection-string").Should().Be(baseConfig.GetBoolean("use-database-number-from-connection-string"));
+ }
+
+ [Fact(DisplayName = "Empty RedisJournalOptions with custom identifier should equal empty config with default fallback")]
+ public void CustomIdJournalOptionsTest()
+ {
+ var options = new RedisJournalOptions(false, "custom");
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(RedisPersistence.DefaultConfig());
+
+ emptyRootConfig.GetString("akka.persistence.journal.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.journal.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.journal.custom");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.journal.redis");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("configuration-string").Should().Be(baseConfig.GetString("configuration-string"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("key-prefix").Should().Be(baseConfig.GetString("key-prefix"));
+ config.GetInt("database").Should().Be(baseConfig.GetInt("database"));
+ config.GetBoolean("use-database-number-from-connection-string").Should().Be(baseConfig.GetBoolean("use-database-number-from-connection-string"));
+ }
+
+ [Fact(DisplayName = "RedisJournalOptions should generate proper config")]
+ public void JournalOptionsTest()
+ {
+ var options = new RedisJournalOptions(true)
+ {
+ Identifier = "custom",
+ AutoInitialize = true,
+ ConfigurationString = "testConfigurationString",
+ KeyPrefix = "testKeyPrefix",
+ Database = 999123,
+ UseDatabaseFromConnectionString = true
+ };
+
+ var baseConfig = options.ToConfig();
+
+ baseConfig.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.custom");
+
+ var config = baseConfig.GetConfig("akka.persistence.journal.custom");
+ config.Should().NotBeNull();
+ config.GetBoolean("auto-initialize").Should().Be(options.AutoInitialize);
+ config.GetString("configuration-string").Should().Be(options.ConfigurationString);
+ config.GetString("key-prefix").Should().Be(options.KeyPrefix);
+ config.GetInt("database").Should().Be(options.Database);
+ config.GetBoolean("use-database-number-from-connection-string").Should().Be(options.UseDatabaseFromConnectionString.Value);
+ }
+
+ const string Json = @"
+ {
+ ""Logging"": {
+ ""LogLevel"": {
+ ""Default"": ""Information"",
+ ""Microsoft.AspNetCore"": ""Warning""
+ }
+ },
+ ""Akka"": {
+ ""JournalOptions"": {
+ ""Identifier"": ""customRedis"",
+ ""AutoInitialize"": true,
+ ""IsDefaultPlugin"": false,
+ ""ConfigurationString"": ""ConfigurationStringFromConfigJson"",
+ ""KeyPrefix"": ""KeyPrefixFromConfigJson"",
+ ""Database"": 123456,
+ ""UseDatabaseFromConnectionString"": true,
+ ""Serializer"": ""TestSerializer"",
+ }
+ }
+ }";
+
+ [Fact(DisplayName = "RedisJournalOptions should be bindable to IConfiguration")]
+ public void JournalOptionsIConfigurationBindingTest()
+ {
+ using var stream = new MemoryStream(Encoding.UTF8.GetBytes(Json));
+ var jsonConfig = new ConfigurationBuilder().AddJsonStream(stream).Build();
+
+ var options = jsonConfig.GetSection("Akka:JournalOptions").Get();
+ options.Identifier.Should().Be("customRedis");
+ options.AutoInitialize.Should().BeTrue();
+ options.IsDefaultPlugin.Should().BeFalse();
+ options.ConfigurationString.Should().Be("ConfigurationStringFromConfigJson");
+ options.KeyPrefix.Should().Be("KeyPrefixFromConfigJson");
+ options.Database.Should().Be(123456);
+ options.UseDatabaseFromConnectionString.Should().BeTrue();
+ options.Serializer.Should().Be("TestSerializer");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.Redis.Tests/Hosting/RedisSnapshotOptionsSpec.cs b/src/Akka.Persistence.Redis.Tests/Hosting/RedisSnapshotOptionsSpec.cs
new file mode 100644
index 0000000..70a9dda
--- /dev/null
+++ b/src/Akka.Persistence.Redis.Tests/Hosting/RedisSnapshotOptionsSpec.cs
@@ -0,0 +1,136 @@
+using System;
+using System.IO;
+using System.Text;
+using Akka.Configuration;
+using Akka.Persistence.Redis.Hosting;
+using FluentAssertions;
+using FluentAssertions.Extensions;
+using Microsoft.Extensions.Configuration;
+using Xunit;
+
+namespace Akka.Persistence.Redis.Tests.Hosting
+{
+ public class RedisSnapshotOptionsSpec
+ {
+ [Fact(DisplayName = "RedisSnapshotOptions as default plugin should generate plugin setting")]
+ public void DefaultPluginSnapshotOptionsTest()
+ {
+ var options = new RedisSnapshotOptions(true);
+ var config = options.ToConfig();
+
+ config.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.redis");
+ config.HasPath("akka.persistence.snapshot-store.redis").Should().BeTrue();
+ }
+
+ [Fact(DisplayName = "Empty RedisSnapshotOptions should equal empty config with default fallback")]
+ public void DefaultSnapshotOptionsTest()
+ {
+ var options = new RedisSnapshotOptions(false);
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(RedisPersistence.DefaultConfig());
+
+ emptyRootConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.snapshot-store.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.snapshot-store.redis");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.snapshot-store.redis");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("configuration-string").Should().Be(baseConfig.GetString("configuration-string"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("key-prefix").Should().Be(baseConfig.GetString("key-prefix"));
+ config.GetInt("database").Should().Be(baseConfig.GetInt("database"));
+ config.GetBoolean("use-database-number-from-connection-string").Should().Be(baseConfig.GetBoolean("use-database-number-from-connection-string"));
+ }
+
+ [Fact(DisplayName = "Empty RedisSnapshotOptions with custom identifier should equal empty config with default fallback")]
+ public void CustomIdSnapshotOptionsTest()
+ {
+ var options = new RedisSnapshotOptions(false, "custom");
+ var emptyRootConfig = options.ToConfig().WithFallback(options.DefaultConfig);
+ var baseRootConfig = Config.Empty
+ .WithFallback(RedisPersistence.DefaultConfig());
+
+ emptyRootConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be(baseRootConfig.GetString("akka.persistence.snapshot-store.plugin"));
+
+ var config = emptyRootConfig.GetConfig("akka.persistence.snapshot-store.custom");
+ var baseConfig = baseRootConfig.GetConfig("akka.persistence.snapshot-store.redis");
+ config.Should().NotBeNull();
+ baseConfig.Should().NotBeNull();
+
+ config.GetString("class").Should().Be(baseConfig.GetString("class"));
+ config.GetString("configuration-string").Should().Be(baseConfig.GetString("configuration-string"));
+ config.GetBoolean("auto-initialize").Should().Be(baseConfig.GetBoolean("auto-initialize"));
+ config.GetString("key-prefix").Should().Be(baseConfig.GetString("key-prefix"));
+ config.GetInt("database").Should().Be(baseConfig.GetInt("database"));
+ config.GetBoolean("use-database-number-from-connection-string").Should().Be(baseConfig.GetBoolean("use-database-number-from-connection-string"));
+ }
+
+ [Fact(DisplayName = "RedisSnapshotOptions should generate proper config")]
+ public void SnapshotOptionsTest()
+ {
+ var options = new RedisSnapshotOptions(true)
+ {
+ Identifier = "custom",
+ AutoInitialize = true,
+ ConfigurationString = "testConfigurationString",
+ KeyPrefix = "testKeyPrefix",
+ Database = 999123,
+ UseDatabaseFromConnectionString = true
+ };
+
+ var baseConfig = options.ToConfig();
+
+ baseConfig.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.custom");
+
+ var config = baseConfig.GetConfig("akka.persistence.snapshot-store.custom");
+ config.Should().NotBeNull();
+ config.GetBoolean("auto-initialize").Should().Be(options.AutoInitialize);
+ config.GetString("configuration-string").Should().Be(options.ConfigurationString);
+ config.GetString("key-prefix").Should().Be(options.KeyPrefix);
+ config.GetInt("database").Should().Be(options.Database);
+ config.GetBoolean("use-database-number-from-connection-string").Should().Be(options.UseDatabaseFromConnectionString.Value);
+ }
+
+ const string Json = @"
+ {
+ ""Logging"": {
+ ""LogLevel"": {
+ ""Default"": ""Information"",
+ ""Microsoft.AspNetCore"": ""Warning""
+ }
+ },
+ ""Akka"": {
+ ""SnapshotOptions"": {
+ ""Identifier"": ""customRedis"",
+ ""AutoInitialize"": true,
+ ""IsDefaultPlugin"": false,
+ ""ConfigurationString"": ""ConfigurationStringFromConfigJson"",
+ ""KeyPrefix"": ""KeyPrefixFromConfigJson"",
+ ""Database"": 123456,
+ ""UseDatabaseFromConnectionString"": true,
+ ""Serializer"": ""TestSerializer"",
+ }
+ }
+ }";
+
+ [Fact(DisplayName = "RedisSnapshotOptions should be bindable to IConfiguration")]
+ public void SnapshotOptionsIConfigurationBindingTest()
+ {
+ using var stream = new MemoryStream(Encoding.UTF8.GetBytes(Json));
+ var jsonConfig = new ConfigurationBuilder().AddJsonStream(stream).Build();
+
+ var options = jsonConfig.GetSection("Akka:SnapshotOptions").Get();
+ options.Identifier.Should().Be("customRedis");
+ options.AutoInitialize.Should().BeTrue();
+ options.IsDefaultPlugin.Should().BeFalse();
+ options.ConfigurationString.Should().Be("ConfigurationStringFromConfigJson");
+ options.KeyPrefix.Should().Be("KeyPrefixFromConfigJson");
+ options.Database.Should().Be(123456);
+ options.UseDatabaseFromConnectionString.Should().BeTrue();
+ options.Serializer.Should().Be("TestSerializer");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Akka.Persistence.Redis/Akka.Persistence.Redis.csproj b/src/Akka.Persistence.Redis/Akka.Persistence.Redis.csproj
index a381829..ae09970 100644
--- a/src/Akka.Persistence.Redis/Akka.Persistence.Redis.csproj
+++ b/src/Akka.Persistence.Redis/Akka.Persistence.Redis.csproj
@@ -4,7 +4,6 @@
false
$(NetStandardVersion)
README.md
- true
False
@@ -19,4 +18,8 @@
+
+
+
+
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 0a5377c..567a613 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -19,8 +19,7 @@
-
-
+
@@ -32,7 +31,6 @@
snupkg
akka;actors;actor model;Akka;concurrency;persistence;eventsource;redis
icon.png
- README.md
https://github.com/akkadotnet/Akka.Persistence.Redis
Apache-2.0
Upgraded to [Akka.NET 1.5.0](https://github.com/akkadotnet/akka.net/releases/tag/1.5.0)
diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props
index 27ae5af..6031410 100644
--- a/src/Directory.Packages.props
+++ b/src/Directory.Packages.props
@@ -7,6 +7,7 @@
+