diff --git a/src/Chronicle.Integrations.MongoDB/src/Chronicle.Integrations.MongoDB.csproj b/src/Chronicle.Integrations.MongoDB/src/Chronicle.Integrations.MongoDB.csproj index 8954157..42ad493 100644 --- a/src/Chronicle.Integrations.MongoDB/src/Chronicle.Integrations.MongoDB.csproj +++ b/src/Chronicle.Integrations.MongoDB/src/Chronicle.Integrations.MongoDB.csproj @@ -18,9 +18,9 @@ - - - + + + diff --git a/src/Chronicle.Integrations.Redis/src/Chronicle.Integrations.Redis.csproj b/src/Chronicle.Integrations.Redis/src/Chronicle.Integrations.Redis.csproj index edb2465..4f0b965 100644 --- a/src/Chronicle.Integrations.Redis/src/Chronicle.Integrations.Redis.csproj +++ b/src/Chronicle.Integrations.Redis/src/Chronicle.Integrations.Redis.csproj @@ -17,10 +17,10 @@ 2.0 - + - - + + diff --git a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLog.cs b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLog.cs index e0bc763..fc823f4 100644 --- a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLog.cs +++ b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLog.cs @@ -31,7 +31,7 @@ public async Task> ReadAsync(SagaId id, Type sagaType) var sagaLogDatas = new List(); var deserializedSagaLogDatas = new List(); var cachedSagaLogDatas = await cache.GetStringAsync(LogId(id, sagaType)); - + if (!string.IsNullOrWhiteSpace(cachedSagaLogDatas)) { sagaLogDatas = JsonConvert.DeserializeObject>(cachedSagaLogDatas); @@ -39,7 +39,7 @@ public async Task> ReadAsync(SagaId id, Type sagaType) { { var message = (sld.Message as JObject)?.ToObject(sld.MessageType); - deserializedSagaLogDatas.Add(new RedisSagaLogData(sld.Id, sld.Type, sld.CreatedAt, message, sld.MessageType)); + deserializedSagaLogDatas.Add(new RedisSagaLogData { SagaId = sld.Id, Type = sld.Type, CreatedAt = sld.CreatedAt, Message = message, MessageType = sld.MessageType }); } }); } @@ -54,7 +54,7 @@ public async Task WriteAsync(ISagaLogData logData) } var sagaLogDatas = (await ReadAsync(logData.Id, logData.Type)).ToList(); - var sagaLogData = new RedisSagaLogData(logData.Id, logData.Type, logData.CreatedAt, logData.Message, logData.Message.GetType()); + var sagaLogData = new RedisSagaLogData { SagaId = logData.Id, Type = logData.Type, CreatedAt = logData.CreatedAt, Message = logData.Message, MessageType = logData.Message.GetType() }; sagaLogDatas.Add(sagaLogData); diff --git a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLogData.cs b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLogData.cs index ac4901e..d88b86c 100644 --- a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLogData.cs +++ b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaLogData.cs @@ -5,23 +5,13 @@ namespace Chronicle.Integrations.Redis.Persistence { internal sealed class RedisSagaLogData : ISagaLogData { - public SagaId Id { get; } - public Type Type { get; } - public long CreatedAt { get; } - public object Message { get; } - public Type MessageType { get; } + public string SagaId { get; set; } + [JsonIgnore] + public SagaId Id => SagaId; + public Type Type { get; set; } + public long CreatedAt { get; set; } + public object Message { get; set; } + public Type MessageType { get; set; } - [JsonConstructor] - public RedisSagaLogData(SagaId id, Type type, long createdAt, object message, Type messageType) - { - Id = id; - Type = type; - CreatedAt = createdAt; - Message = message; - MessageType = messageType; - } - - public static ISagaLogData Create(SagaId sagaId, Type sagaType, object message) - => new RedisSagaLogData(sagaId, sagaType, DateTimeOffset.Now.ToUnixTimeMilliseconds(), message, message.GetType()); } } \ No newline at end of file diff --git a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaState.cs b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaState.cs index 94af561..5e12f93 100644 --- a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaState.cs +++ b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaState.cs @@ -5,18 +5,13 @@ namespace Chronicle.Integrations.Redis.Persistence { internal sealed class RedisSagaState : ISagaState { - public SagaId Id { get; } - public Type Type { get; } - public SagaStates State { get; private set; } - public object Data { get; private set; } - public Type DataType { get; } - - [JsonConstructor] - public RedisSagaState(SagaId id, Type type, SagaStates state, object data = null, Type dataType = null) - => (Id, Type, State, Data, DataType) = (id, type, state, data, dataType); - - public static ISagaState Create(SagaId sagaId, Type sagaType, SagaStates state, object data = null, Type dataType = null) - => new RedisSagaState(sagaId, sagaType, state, data, dataType); + public string SagaId { get; set; } + [JsonIgnore] + public SagaId Id => SagaId; + public Type Type { get; set; } + public SagaStates State { get; set; } + public object Data { get; set; } + public Type DataType { get; set; } public void Update(SagaStates state, object data = null) { diff --git a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaStateRepository.cs b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaStateRepository.cs index 9df3d68..5282f28 100644 --- a/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaStateRepository.cs +++ b/src/Chronicle.Integrations.Redis/src/Persistence/RedisSagaStateRepository.cs @@ -12,7 +12,7 @@ internal sealed class RedisSagaStateRepository : ISagaStateRepository public RedisSagaStateRepository(IDistributedCache cache) => _cache = cache; - + public async Task ReadAsync(SagaId sagaId, Type sagaType) { if (string.IsNullOrWhiteSpace(sagaId)) @@ -26,7 +26,7 @@ public async Task ReadAsync(SagaId sagaId, Type sagaType) RedisSagaState state = null; var cachedSagaState = await _cache.GetStringAsync(StateId(sagaId, sagaType)); - + if (!string.IsNullOrWhiteSpace(cachedSagaState)) { state = JsonConvert.DeserializeObject(cachedSagaState); @@ -42,8 +42,7 @@ public async Task WriteAsync(ISagaState state) throw new ChronicleException($"{nameof(state)} was null."); } - var sagaState = new RedisSagaState(state.Id, state.Type, state.State, state.Data, state.Data.GetType()); - + var sagaState = new RedisSagaState { SagaId = state.Id, Type = state.Type, State = state.State, Data = state.Data, DataType = state.Data.GetType() }; var serializedSagaState = JsonConvert.SerializeObject(sagaState); await _cache.SetStringAsync(StateId(state.Id, state.Type), serializedSagaState); diff --git a/src/Chronicle.Tests/Chronicle.Tests.csproj b/src/Chronicle.Tests/Chronicle.Tests.csproj index 039cdd3..ed48e16 100644 --- a/src/Chronicle.Tests/Chronicle.Tests.csproj +++ b/src/Chronicle.Tests/Chronicle.Tests.csproj @@ -6,11 +6,14 @@ - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + diff --git a/src/Chronicle.sln b/src/Chronicle.sln index a10f270..23fb6de 100644 --- a/src/Chronicle.sln +++ b/src/Chronicle.sln @@ -11,6 +11,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chronicle.Tests", "Chronicl EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chronicle.Integrations.MongoDB", "Chronicle.Integrations.MongoDB\src\Chronicle.Integrations.MongoDB.csproj", "{DFB344D1-3121-43F9-9E66-20BC6B0DD9CC}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Chronicle.Integrations.Redis", "Chronicle.Integrations.Redis", "{3B661443-315C-4A0F-A67E-62A792AF35D3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chronicle.Integrations.Redis", "Chronicle.Integrations.Redis\src\Chronicle.Integrations.Redis.csproj", "{755F7A4D-7FC3-444E-86D3-528787961B2F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,6 +73,18 @@ Global {DFB344D1-3121-43F9-9E66-20BC6B0DD9CC}.Release|x64.Build.0 = Release|Any CPU {DFB344D1-3121-43F9-9E66-20BC6B0DD9CC}.Release|x86.ActiveCfg = Release|Any CPU {DFB344D1-3121-43F9-9E66-20BC6B0DD9CC}.Release|x86.Build.0 = Release|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Debug|x64.ActiveCfg = Debug|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Debug|x64.Build.0 = Debug|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Debug|x86.ActiveCfg = Debug|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Debug|x86.Build.0 = Debug|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Release|Any CPU.Build.0 = Release|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Release|x64.ActiveCfg = Release|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Release|x64.Build.0 = Release|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Release|x86.ActiveCfg = Release|Any CPU + {755F7A4D-7FC3-444E-86D3-528787961B2F}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -76,4 +92,7 @@ Global GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F6DDA96B-847E-4F6D-86EC-126800155219} EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {755F7A4D-7FC3-444E-86D3-528787961B2F} = {3B661443-315C-4A0F-A67E-62A792AF35D3} + EndGlobalSection EndGlobal diff --git a/src/Chronicle/Builders/ChronicleBuilder.cs b/src/Chronicle/Builders/ChronicleBuilder.cs index 5a4e614..3041cc5 100644 --- a/src/Chronicle/Builders/ChronicleBuilder.cs +++ b/src/Chronicle/Builders/ChronicleBuilder.cs @@ -28,5 +28,15 @@ public IChronicleBuilder UseSagaStateRepository() where TRepository Services.AddTransient(typeof(ISagaStateRepository), typeof(TRepository)); return this; } + + public IChronicleBuilder UseChronicleConfiguration(IChronicleConfiguration configuration) + { + if (configuration is null) + { + configuration = new ChronicleConfiguration(); + } + Services.AddSingleton(configuration); + return this; + } } } diff --git a/src/Chronicle/Chronicle.csproj b/src/Chronicle/Chronicle.csproj index aa237dc..1142558 100644 --- a/src/Chronicle/Chronicle.csproj +++ b/src/Chronicle/Chronicle.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/src/Chronicle/ChronicleConfiguration.cs b/src/Chronicle/ChronicleConfiguration.cs new file mode 100644 index 0000000..7a03673 --- /dev/null +++ b/src/Chronicle/ChronicleConfiguration.cs @@ -0,0 +1,8 @@ +namespace Chronicle +{ + + public class ChronicleConfiguration : IChronicleConfiguration + { + public bool AllowConcurrentWrites { get; set; } = true; + } +} \ No newline at end of file diff --git a/src/Chronicle/Extensions.cs b/src/Chronicle/Extensions.cs index e6b6ba8..f5afe5e 100644 --- a/src/Chronicle/Extensions.cs +++ b/src/Chronicle/Extensions.cs @@ -17,6 +17,7 @@ public static IServiceCollection AddChronicle(this IServiceCollection services, services.AddTransient(); services.AddTransient(); services.AddTransient(); + services.AddSingleton(); var chronicleBuilder = new ChronicleBuilder(services); diff --git a/src/Chronicle/IChronicleBuilder.cs b/src/Chronicle/IChronicleBuilder.cs index c24b649..aa130fe 100644 --- a/src/Chronicle/IChronicleBuilder.cs +++ b/src/Chronicle/IChronicleBuilder.cs @@ -8,5 +8,6 @@ public interface IChronicleBuilder IChronicleBuilder UseInMemoryPersistence(); IChronicleBuilder UseSagaLog() where TSagaLog : ISagaLog; IChronicleBuilder UseSagaStateRepository() where TRepository : ISagaStateRepository; + IChronicleBuilder UseChronicleConfiguration(IChronicleConfiguration configuration); } } diff --git a/src/Chronicle/IChronicleConfiguration.cs b/src/Chronicle/IChronicleConfiguration.cs new file mode 100644 index 0000000..aadfce9 --- /dev/null +++ b/src/Chronicle/IChronicleConfiguration.cs @@ -0,0 +1,7 @@ +namespace Chronicle +{ + public interface IChronicleConfiguration + { + bool AllowConcurrentWrites { get; set; } + } +} \ No newline at end of file diff --git a/src/Chronicle/Managers/SagaCoordinator.cs b/src/Chronicle/Managers/SagaCoordinator.cs index 9e877b8..a9bef2a 100644 --- a/src/Chronicle/Managers/SagaCoordinator.cs +++ b/src/Chronicle/Managers/SagaCoordinator.cs @@ -53,7 +53,7 @@ private async Task ProcessAsync(TMessage message, ISagaAction(TMessage message, ISaga saga, ISaga state.Update(saga.State, updatedSagaData); var logData = SagaLogData.Create(saga.Id, sagaType, message); - var persistenceTasks = new [] + if (_configuration.AllowConcurrentWrites) { - _repository.WriteAsync(state), - _log.WriteAsync(logData) - }; + var persistenceTasks = new[] + { + _repository.WriteAsync(state), + _log.WriteAsync(logData) + }; - await Task.WhenAll(persistenceTasks).ConfigureAwait(false); + await Task.WhenAll(persistenceTasks).ConfigureAwait(false); + } + else + { + await _repository.WriteAsync(state); + await _log.WriteAsync(logData); + } } } }