From 06c1fcc2be37aeb97049a2ca688b455d9e624466 Mon Sep 17 00:00:00 2001
From: adamjstone <8525409+adamjstone@users.noreply.github.com>
Date: Thu, 26 Nov 2020 18:44:12 -0600
Subject: [PATCH] #371 Introduce caching abstractions and mediator extensions.
---
RapidField.SolidInstruments.sln | 3 +
...eld.SolidInstruments.Command.Extensions.md | 31 ++
.../RapidField.SolidInstruments.Command.md | 6 +
...apidField.SolidInstruments.Core.Caching.md | 31 ++
.../RapidField.SolidInstruments.Core.md | 6 +
...d.SolidInstruments.Messaging.Extensions.md | 31 ++
.../RapidField.SolidInstruments.Messaging.md | 6 +
en-US_User.dic | 1 +
.../ApplicationServiceExecutor.cs | 3 +-
.../ApplicationServiceExecutor.cs | 3 +-
.../BinaryTreeNode.cs | 4 +-
.../AutofacCommandHandlerModule.cs | 4 +
.../Extensions/ContainerBuilderExtensions.cs | 14 +
.../DotNetNativeCommandHandlerModule.cs | 4 +
.../IServiceCollectionExtensions.cs | 18 +
.../CommandHandlingException.cs | 12 +
.../CommandRegister.cs | 32 ++
.../GetConfigurationObjectCommand.cs | 70 +++
.../GetConfigurationObjectCommandHandler.cs | 98 ++++
.../GetConfigurationObjectCommandTarget.cs | 40 ++
.../GetConfigurationSectionCommand.cs | 69 +++
.../GetConfigurationSectionCommandHandler.cs | 53 ++
.../GetConfigurationValueCommand.cs | 350 +++++++++++++
.../GetConfigurationValueCommandHandler.cs | 53 ++
.../GetConnectionStringCommand.cs | 64 +++
.../GetConnectionStringCommandHandler.cs | 53 ++
.../IGetConfigurationObjectCommand.cs | 38 ++
.../Extensions/ICommandMediatorExtensions.cs | 200 ++++++++
.../Extensions/ICommandRegisterExtensions.cs | 114 +++++
.../ICommandRegister.cs | 23 +
...RapidField.SolidInstruments.Command.csproj | 3 +
.../Caching/CacheAccessException.cs | 85 ++++
.../Caching/CacheClient.cs | 344 +++++++++++++
.../Caching/ICacheClient.cs | 107 ++++
.../Caching/ICacheReader.cs | 44 ++
.../Caching/ICacheWriter.cs | 41 ++
.../Caching/IDistributedCacheClient.cs | 13 +
.../Caching/IInMemoryCacheClient.cs | 25 +
.../Caching/InMemoryCacheClient.cs | 243 +++++++++
.../Caching/InMemoryCachingStrategy.cs | 40 ++
.../ConcurrencyControlOperationException.cs | 2 +-
.../Concurrency/ConcurrencyControlToken.cs | 2 +-
.../InMemoryCachingStrategyExtensions.cs | 317 ++++++++++++
.../Extensions/ObjectExtensions.cs | 225 ++++++++
.../RapidField.SolidInstruments.Core.csproj | 1 +
.../Hashing/HashingProcessor.cs | 2 +-
.../Secrets/ExportedSecret.cs | 2 +-
.../SecretStoreFilePersistenceVehicle.cs | 2 +-
.../AssemblyAttributes.cs | 3 +-
.../EventRegister.cs | 32 ++
.../Extensions/ICommandMediatorExtensions.cs | 183 +++++++
.../Extensions/IEventRegisterExtensions.cs | 206 ++++++++
.../IEventRegister.cs | 24 +
.../AutofacServiceInjector.cs | 4 +-
.../DependencyEngine.cs | 2 +-
.../Extensions/ContainerBuilderExtensions.cs | 2 +
.../Extensions/ContainerBuilderExtensions.cs | 2 +
.../AzureServiceBusListeningFacade.cs | 2 +-
.../IServiceCollectionExtensions.cs | 2 +
.../IServiceCollectionExtensions.cs | 2 +
.../CommandMessageRegister.cs | 41 ++
.../CommandMessages/TextualCommandMessage.cs | 41 ++
.../EventMessageRegister.cs | 41 ++
.../Extensions/ICommandMediatorExtensions.cs | 479 ++++++++++++++++++
.../ICommandMessageRegisterExtensions.cs | 54 ++
.../IEventMessageRegisterExtensions.cs | 13 +
.../Extensions/IMessageRegisterExtensions.cs | 13 +
.../IRequestMessageRegisterExtensions.cs | 43 ++
.../ICommandMessageRegister.cs | 32 ++
.../IEventMessageRegister.cs | 33 ++
.../IMessageRegister.cs | 48 ++
.../IRequestMessageRegister.cs | 25 +
.../MessageRegister.cs | 58 +++
.../RequestMessageRegister.cs | 32 ++
.../TransportPrimitives/PrimitiveMessage.cs | 8 +-
.../Channel.cs | 2 +-
.../DomainModelHttpApiController.cs | 40 ++
.../HttpApiController.cs | 145 +++++-
.../RapidField.SolidInstruments.Web.csproj | 1 +
.../Caching/InMemoryCacheClientTests.cs | 64 +++
.../Extensions/ObjectExtensionsTests.cs | 15 +
.../SimulatedObject.cs | 8 +-
.../SimulatedObject.cs | 8 +-
83 files changed, 4615 insertions(+), 25 deletions(-)
create mode 100644 doc/namespaces/RapidField.SolidInstruments.Command.Extensions.md
create mode 100644 doc/namespaces/RapidField.SolidInstruments.Core.Caching.md
create mode 100644 doc/namespaces/RapidField.SolidInstruments.Messaging.Extensions.md
create mode 100644 src/RapidField.SolidInstruments.Command/CommandRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommand.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandHandler.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandTarget.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommand.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommandHandler.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommand.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommandHandler.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommand.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommandHandler.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Configuration/IGetConfigurationObjectCommand.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Extensions/ICommandMediatorExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.Command/Extensions/ICommandRegisterExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.Command/ICommandRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/CacheAccessException.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/CacheClient.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/ICacheClient.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/ICacheReader.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/ICacheWriter.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/IDistributedCacheClient.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/IInMemoryCacheClient.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/InMemoryCacheClient.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Caching/InMemoryCachingStrategy.cs
create mode 100644 src/RapidField.SolidInstruments.Core/Extensions/InMemoryCachingStrategyExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.EventAuthoring/EventRegister.cs
create mode 100644 src/RapidField.SolidInstruments.EventAuthoring/Extensions/ICommandMediatorExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.EventAuthoring/Extensions/IEventRegisterExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.EventAuthoring/IEventRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/CommandMessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/CommandMessages/TextualCommandMessage.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/EventMessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMediatorExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMessageRegisterExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/Extensions/IEventMessageRegisterExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/Extensions/IMessageRegisterExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/Extensions/IRequestMessageRegisterExtensions.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/ICommandMessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/IEventMessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/IMessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/IRequestMessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/MessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Messaging/RequestMessageRegister.cs
create mode 100644 src/RapidField.SolidInstruments.Web/DomainModelHttpApiController.cs
create mode 100644 test/RapidField.SolidInstruments.Core.UnitTests/Caching/InMemoryCacheClientTests.cs
diff --git a/RapidField.SolidInstruments.sln b/RapidField.SolidInstruments.sln
index 1db19f90..61894018 100644
--- a/RapidField.SolidInstruments.sln
+++ b/RapidField.SolidInstruments.sln
@@ -193,8 +193,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "namespaces", "namespaces",
doc\namespaces\RapidField.SolidInstruments.Command.Autofac.md = doc\namespaces\RapidField.SolidInstruments.Command.Autofac.md
doc\namespaces\RapidField.SolidInstruments.Command.DotNetNative.Extensions.md = doc\namespaces\RapidField.SolidInstruments.Command.DotNetNative.Extensions.md
doc\namespaces\RapidField.SolidInstruments.Command.DotNetNative.md = doc\namespaces\RapidField.SolidInstruments.Command.DotNetNative.md
+ doc\namespaces\RapidField.SolidInstruments.Command.Extensions.md = doc\namespaces\RapidField.SolidInstruments.Command.Extensions.md
doc\namespaces\RapidField.SolidInstruments.Command.md = doc\namespaces\RapidField.SolidInstruments.Command.md
doc\namespaces\RapidField.SolidInstruments.Core.ArgumentValidation.md = doc\namespaces\RapidField.SolidInstruments.Core.ArgumentValidation.md
+ doc\namespaces\RapidField.SolidInstruments.Core.Caching.md = doc\namespaces\RapidField.SolidInstruments.Core.Caching.md
doc\namespaces\RapidField.SolidInstruments.Core.Concurrency.md = doc\namespaces\RapidField.SolidInstruments.Core.Concurrency.md
doc\namespaces\RapidField.SolidInstruments.Core.Domain.md = doc\namespaces\RapidField.SolidInstruments.Core.Domain.md
doc\namespaces\RapidField.SolidInstruments.Core.Extensions.md = doc\namespaces\RapidField.SolidInstruments.Core.Extensions.md
@@ -250,6 +252,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "namespaces", "namespaces",
doc\namespaces\RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.Extensions.md = doc\namespaces\RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.Extensions.md
doc\namespaces\RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.md = doc\namespaces\RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.md
doc\namespaces\RapidField.SolidInstruments.Messaging.EventMessages.md = doc\namespaces\RapidField.SolidInstruments.Messaging.EventMessages.md
+ doc\namespaces\RapidField.SolidInstruments.Messaging.Extensions.md = doc\namespaces\RapidField.SolidInstruments.Messaging.Extensions.md
doc\namespaces\RapidField.SolidInstruments.Messaging.InMemory.md = doc\namespaces\RapidField.SolidInstruments.Messaging.InMemory.md
doc\namespaces\RapidField.SolidInstruments.Messaging.md = doc\namespaces\RapidField.SolidInstruments.Messaging.md
doc\namespaces\RapidField.SolidInstruments.Messaging.RabbitMq.md = doc\namespaces\RapidField.SolidInstruments.Messaging.RabbitMq.md
diff --git a/doc/namespaces/RapidField.SolidInstruments.Command.Extensions.md b/doc/namespaces/RapidField.SolidInstruments.Command.Extensions.md
new file mode 100644
index 00000000..c3eb1437
--- /dev/null
+++ b/doc/namespaces/RapidField.SolidInstruments.Command.Extensions.md
@@ -0,0 +1,31 @@
+---
+uid: RapidField.SolidInstruments.Command.Extensions
+summary: *content
+---
+
+
+
+Exposes extensions that support the command and mediator patterns.
+
+
+
+![Command label](../images/Label.Command.300w.png)
+- - -
+
+### Installation
+
+This library is available via [**NuGet**](https://docs.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio). Use one of the commands below to download and install the library and all of its dependencies.
+
+###### .NET CLI
+
+```shell
+dotnet add package RapidField.SolidInstruments.Command
+```
+
+###### NuGet Package Manager
+
+```shell
+Install-Package RapidField.SolidInstruments.Command
+```
\ No newline at end of file
diff --git a/doc/namespaces/RapidField.SolidInstruments.Command.md b/doc/namespaces/RapidField.SolidInstruments.Command.md
index 79aa29af..e78dac51 100644
--- a/doc/namespaces/RapidField.SolidInstruments.Command.md
+++ b/doc/namespaces/RapidField.SolidInstruments.Command.md
@@ -108,4 +108,10 @@ Exposes the Autofac integration for the Solid Instruments implementations of the
Exposes the native .NET IoC integration for the Solid Instruments implementations of the command and mediator patterns.
+
+
+#### [RapidField.SolidInstruments.Command.Extensions](https://www.solidinstruments.com/api/RapidField.SolidInstruments.Command.Extensions.html)
+
+
+Exposes extensions that support the command and mediator patterns.
\ No newline at end of file
diff --git a/doc/namespaces/RapidField.SolidInstruments.Core.Caching.md b/doc/namespaces/RapidField.SolidInstruments.Core.Caching.md
new file mode 100644
index 00000000..5b874c0b
--- /dev/null
+++ b/doc/namespaces/RapidField.SolidInstruments.Core.Caching.md
@@ -0,0 +1,31 @@
+---
+uid: RapidField.SolidInstruments.Core.Caching
+summary: *content
+---
+
+
+
+Exposes configurable clients for accessing cached data.
+
+
+
+![Core label](../images/Label.Core.300w.png)
+- - -
+
+### Installation
+
+This library is available via [**NuGet**](https://docs.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio). Use one of the commands below to download and install the library and all of its dependencies.
+
+###### .NET CLI
+
+```shell
+dotnet add package RapidField.SolidInstruments.Core
+```
+
+###### NuGet Package Manager
+
+```shell
+Install-Package RapidField.SolidInstruments.Core
+```
\ No newline at end of file
diff --git a/doc/namespaces/RapidField.SolidInstruments.Core.md b/doc/namespaces/RapidField.SolidInstruments.Core.md
index 538f1f28..0a5dedae 100644
--- a/doc/namespaces/RapidField.SolidInstruments.Core.md
+++ b/doc/namespaces/RapidField.SolidInstruments.Core.md
@@ -38,6 +38,12 @@ Install-Package RapidField.SolidInstruments.Core
Defines a fluent pattern for evaluating argument validity.
+#### [RapidField.SolidInstruments.Core.Caching](https://www.solidinstruments.com/api/RapidField.SolidInstruments.Core.Caching.html)
+
+
+Exposes configurable clients for accessing cached data.
+
+
#### [RapidField.SolidInstruments.Core.Concurrency](https://www.solidinstruments.com/api/RapidField.SolidInstruments.Core.Concurrency.html)
diff --git a/doc/namespaces/RapidField.SolidInstruments.Messaging.Extensions.md b/doc/namespaces/RapidField.SolidInstruments.Messaging.Extensions.md
new file mode 100644
index 00000000..93c5b4b1
--- /dev/null
+++ b/doc/namespaces/RapidField.SolidInstruments.Messaging.Extensions.md
@@ -0,0 +1,31 @@
+---
+uid: RapidField.SolidInstruments.Messaging.Extensions
+summary: *content
+---
+
+
+
+Exposes extensions that support messaging abstractions.
+
+
+
+![Messaging label](../images/Label.Messaging.300w.png)
+- - -
+
+### Installation
+
+This library is available via [**NuGet**](https://docs.microsoft.com/en-us/nuget/quickstart/install-and-use-a-package-in-visual-studio). Use one of the commands below to download and install the library and all of its dependencies.
+
+###### .NET CLI
+
+```shell
+dotnet add package RapidField.SolidInstruments.Messaging
+```
+
+###### NuGet Package Manager
+
+```shell
+Install-Package RapidField.SolidInstruments.Messaging
+```
\ No newline at end of file
diff --git a/doc/namespaces/RapidField.SolidInstruments.Messaging.md b/doc/namespaces/RapidField.SolidInstruments.Messaging.md
index 93daf899..1278aedd 100644
--- a/doc/namespaces/RapidField.SolidInstruments.Messaging.md
+++ b/doc/namespaces/RapidField.SolidInstruments.Messaging.md
@@ -56,6 +56,12 @@ Exposes the native .NET IoC integration for the Solid Instruments messaging abst
Exposes various event message types.
+#### [RapidField.SolidInstruments.Messaging.Extensions](https://www.solidinstruments.com/api/RapidField.SolidInstruments.Messaging.Extensions.html)
+
+
+Exposes extensions that support messaging abstractions.
+
+
#### [RapidField.SolidInstruments.Messaging.Service](https://www.solidinstruments.com/api/RapidField.SolidInstruments.Messaging.Service.html)
diff --git a/en-US_User.dic b/en-US_User.dic
index 8ee46e27..340dd58f 100644
--- a/en-US_User.dic
+++ b/en-US_User.dic
@@ -47,6 +47,7 @@ composable
configurator
configurators
contentfiles
+creational
cryptographic
cryptographically
csharp
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs
index 627242b0..5856a360 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs
@@ -7,6 +7,7 @@
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.InversionOfControl;
using RapidField.SolidInstruments.Messaging.DotNetNative.Service;
+using RapidField.SolidInstruments.Messaging.Extensions;
using RapidField.SolidInstruments.Messaging.Service;
using RapidField.SolidInstruments.Service;
using System;
@@ -269,7 +270,7 @@ private static void TestServiceBusConnectivity(IDependencyScope dependencyScope)
{
var mediator = dependencyScope.Resolve();
var pingRequestCorrelationIdentifier = Guid.NewGuid();
- var pingResponseMessage = mediator.Process(new PingRequestMessage(pingRequestCorrelationIdentifier));
+ var pingResponseMessage = mediator.TransmitRequestMessage(register => register.Ping(pingRequestCorrelationIdentifier));
if (pingResponseMessage is null || pingResponseMessage.CorrelationIdentifier != pingRequestCorrelationIdentifier)
{
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationServiceExecutor.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationServiceExecutor.cs
index f74f5453..e78fdb8b 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationServiceExecutor.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationServiceExecutor.cs
@@ -7,6 +7,7 @@
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.InversionOfControl;
using RapidField.SolidInstruments.Messaging.DotNetNative.Service;
+using RapidField.SolidInstruments.Messaging.Extensions;
using RapidField.SolidInstruments.Messaging.Service;
using RapidField.SolidInstruments.Service;
using System;
@@ -196,7 +197,7 @@ private static void TestServiceBusConnectivity(IDependencyScope dependencyScope)
{
var mediator = dependencyScope.Resolve();
var pingRequestCorrelationIdentifier = Guid.NewGuid();
- var pingResponseMessage = mediator.Process(new PingRequestMessage(pingRequestCorrelationIdentifier));
+ var pingResponseMessage = mediator.TransmitRequestMessage(register => register.Ping(pingRequestCorrelationIdentifier));
if (pingResponseMessage is null || pingResponseMessage.CorrelationIdentifier != pingRequestCorrelationIdentifier)
{
diff --git a/src/RapidField.SolidInstruments.Collections/BinaryTreeNode.cs b/src/RapidField.SolidInstruments.Collections/BinaryTreeNode.cs
index d67698dd..0745a961 100644
--- a/src/RapidField.SolidInstruments.Collections/BinaryTreeNode.cs
+++ b/src/RapidField.SolidInstruments.Collections/BinaryTreeNode.cs
@@ -118,7 +118,7 @@ public BinaryTreeNode(T value, BinaryTreeNode leftChild, BinaryTreeNode ri
private BinaryTreeNode(T value, BinaryTreeNode leftChild, BinaryTreeNode rightChild, Boolean rejectNullLeftChild, Boolean rejectNullRightChild)
: base(value, 2)
{
- if ((leftChild is null) == false)
+ if (leftChild is not null)
{
if (AddChild(leftChild) == false)
{
@@ -130,7 +130,7 @@ private BinaryTreeNode(T value, BinaryTreeNode leftChild, BinaryTreeNode r
leftChild.RejectIf().IsNull(nameof(leftChild));
}
- if ((rightChild is null) == false)
+ if (rightChild is not null)
{
if (AddChild(rightChild) == false)
{
diff --git a/src/RapidField.SolidInstruments.Command.Autofac/AutofacCommandHandlerModule.cs b/src/RapidField.SolidInstruments.Command.Autofac/AutofacCommandHandlerModule.cs
index 20d308da..31c261aa 100644
--- a/src/RapidField.SolidInstruments.Command.Autofac/AutofacCommandHandlerModule.cs
+++ b/src/RapidField.SolidInstruments.Command.Autofac/AutofacCommandHandlerModule.cs
@@ -7,6 +7,7 @@
using RapidField.SolidInstruments.Command.Autofac.Extensions;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using RapidField.SolidInstruments.InversionOfControl.Autofac;
+using RapidField.SolidInstruments.InversionOfControl.Autofac.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -111,6 +112,9 @@ protected AutofacCommandHandlerModule(IConfiguration applicationConfiguration, A
///
protected sealed override void Configure(ContainerBuilder configurator, IConfiguration applicationConfiguration)
{
+ configurator.RegisterApplicationConfiguration(applicationConfiguration);
+ configurator.RegisterConfigurationCommandHandlers();
+
foreach (var commandHandlerType in MatchedTypes)
{
var commandHandlerInterfaceType = commandHandlerType.GetInterfaces().Where(implementedInterface => CommandHandlerInterfaceType.IsAssignableFrom(implementedInterface) && implementedInterface.GenericTypeArguments.Length == 1).FirstOrDefault();
diff --git a/src/RapidField.SolidInstruments.Command.Autofac/Extensions/ContainerBuilderExtensions.cs b/src/RapidField.SolidInstruments.Command.Autofac/Extensions/ContainerBuilderExtensions.cs
index 43794122..850df9de 100644
--- a/src/RapidField.SolidInstruments.Command.Autofac/Extensions/ContainerBuilderExtensions.cs
+++ b/src/RapidField.SolidInstruments.Command.Autofac/Extensions/ContainerBuilderExtensions.cs
@@ -3,6 +3,7 @@
// =================================================================================================================================
using Autofac;
+using RapidField.SolidInstruments.Command.Configuration;
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using System;
@@ -58,6 +59,19 @@ public static void RegisterCommandHandler(this ContainerBuilder target, Type com
target.RegisterType(commandHandlerType.RejectIf().IsNull(nameof(commandHandlerType)).OrIf().IsNotSupportedType(commandHandlerInterfaceType, nameof(commandHandlerType))).IfNotRegistered(commandHandlerType).As(commandHandlerInterfaceType).InstancePerDependency();
}
+ ///
+ /// Registers transient command handlers for retrieving configuration sections and values.
+ ///
+ ///
+ /// The current .
+ ///
+ public static void RegisterConfigurationCommandHandlers(this ContainerBuilder target)
+ {
+ target.RegisterCommandHandler();
+ target.RegisterCommandHandler();
+ target.RegisterCommandHandler();
+ }
+
///
/// Represents the type.
///
diff --git a/src/RapidField.SolidInstruments.Command.DotNetNative/DotNetNativeCommandHandlerModule.cs b/src/RapidField.SolidInstruments.Command.DotNetNative/DotNetNativeCommandHandlerModule.cs
index 3338b7ab..96f17185 100644
--- a/src/RapidField.SolidInstruments.Command.DotNetNative/DotNetNativeCommandHandlerModule.cs
+++ b/src/RapidField.SolidInstruments.Command.DotNetNative/DotNetNativeCommandHandlerModule.cs
@@ -7,6 +7,7 @@
using RapidField.SolidInstruments.Command.DotNetNative.Extensions;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using RapidField.SolidInstruments.InversionOfControl.DotNetNative;
+using RapidField.SolidInstruments.InversionOfControl.DotNetNative.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -111,6 +112,9 @@ protected DotNetNativeCommandHandlerModule(IConfiguration applicationConfigurati
///
protected sealed override void Configure(ServiceCollection configurator, IConfiguration applicationConfiguration)
{
+ configurator.AddApplicationConfiguration(applicationConfiguration);
+ configurator.AddConfigurationCommandHandlers();
+
foreach (var commandHandlerType in MatchedTypes)
{
var commandHandlerInterfaceType = commandHandlerType.GetInterfaces().Where(implementedInterface => CommandHandlerInterfaceType.IsAssignableFrom(implementedInterface) && implementedInterface.GenericTypeArguments.Length == 1).FirstOrDefault();
diff --git a/src/RapidField.SolidInstruments.Command.DotNetNative/Extensions/IServiceCollectionExtensions.cs b/src/RapidField.SolidInstruments.Command.DotNetNative/Extensions/IServiceCollectionExtensions.cs
index 0762ca62..81835b35 100644
--- a/src/RapidField.SolidInstruments.Command.DotNetNative/Extensions/IServiceCollectionExtensions.cs
+++ b/src/RapidField.SolidInstruments.Command.DotNetNative/Extensions/IServiceCollectionExtensions.cs
@@ -4,6 +4,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using RapidField.SolidInstruments.Command.Configuration;
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using System;
@@ -66,6 +67,23 @@ public static IServiceCollection AddCommandHandler(this IServiceCollection targe
return target;
}
+ ///
+ /// Registers transient command handlers for retrieving configuration sections and values.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// The resulting .
+ ///
+ public static IServiceCollection AddConfigurationCommandHandlers(this IServiceCollection target)
+ {
+ target.AddCommandHandler();
+ target.AddCommandHandler();
+ target.AddCommandHandler();
+ return target;
+ }
+
///
/// Represents the type.
///
diff --git a/src/RapidField.SolidInstruments.Command/CommandHandlingException.cs b/src/RapidField.SolidInstruments.Command/CommandHandlingException.cs
index dedb235e..31164163 100644
--- a/src/RapidField.SolidInstruments.Command/CommandHandlingException.cs
+++ b/src/RapidField.SolidInstruments.Command/CommandHandlingException.cs
@@ -45,6 +45,18 @@ public CommandHandlingException(String message)
return;
}
+ ///
+ /// Initializes a new instance of the
+ ///
+ ///
+ /// The exception that is the cause of the current exception.
+ ///
+ public CommandHandlingException(Exception innerException)
+ : this(commandType: null, innerException: innerException)
+ {
+ return;
+ }
+
///
/// Initializes a new instance of the
///
diff --git a/src/RapidField.SolidInstruments.Command/CommandRegister.cs b/src/RapidField.SolidInstruments.Command/CommandRegister.cs
new file mode 100644
index 00000000..f97903c6
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/CommandRegister.cs
@@ -0,0 +1,32 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Command
+{
+ ///
+ /// Represents an extensible catalog of available commands.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public sealed class CommandRegister : ICommandRegister
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [DebuggerHidden]
+ private CommandRegister()
+ {
+ return;
+ }
+
+ ///
+ /// Represents a singleton instance of the class.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ internal static readonly ICommandRegister Instance = new CommandRegister();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommand.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommand.cs
new file mode 100644
index 00000000..5053387d
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommand.cs
@@ -0,0 +1,70 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Core;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using System;
+using System.Runtime.Serialization;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Represents a command that retrieves and deserializes a configuration value or section.
+ ///
+ ///
+ /// is the default implementation of
+ /// .
+ ///
+ ///
+ /// The type of the configuration object that is produced by interrogating the specified configuration key and target.
+ ///
+ [DataContract]
+ public abstract class GetConfigurationObjectCommand : Command, IGetConfigurationObjectCommand
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A textual key for the configuration value or section to retrieve.
+ ///
+ ///
+ /// The target type of the command.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// is equal to .
+ ///
+ protected GetConfigurationObjectCommand(String key, GetConfigurationObjectCommandTarget target)
+ : base()
+ {
+ Key = key.RejectIf().IsNullOrEmpty(nameof(key));
+ Target = target.RejectIf().IsEqualToValue(GetConfigurationObjectCommandTarget.Unspecified, nameof(target));
+ }
+
+ ///
+ /// Gets or sets a textual key for the configuration value or section to retrieve.
+ ///
+ [DataMember]
+ public String Key
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Gets or sets the target type of the current .
+ ///
+ [DataMember]
+ public GetConfigurationObjectCommandTarget Target
+ {
+ get;
+ set;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandHandler.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandHandler.cs
new file mode 100644
index 00000000..b595f207
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandHandler.cs
@@ -0,0 +1,98 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.Extensions.Configuration;
+using RapidField.SolidInstruments.Core;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using System;
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Processes a single .
+ ///
+ ///
+ /// The type of the command that is processed by the handler.
+ ///
+ ///
+ /// The type of the configuration object that is produced by interrogating the specified configuration key and target.
+ ///
+ public abstract class GetConfigurationObjectCommandHandler : CommandHandler
+ where TCommand : class, IGetConfigurationObjectCommand
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ protected GetConfigurationObjectCommandHandler(ICommandMediator mediator, IConfiguration applicationConfiguration)
+ : base(mediator)
+ {
+ ApplicationConfiguration = applicationConfiguration.RejectIf().IsNull(nameof(applicationConfiguration)).TargetArgument;
+ }
+
+ ///
+ /// Converts the specified configuration object to the appropriate result type.
+ ///
+ ///
+ /// The configuration value or section to convert.
+ ///
+ ///
+ /// The converted result object.
+ ///
+ protected abstract TResult ConvertConfigurationObject(Object configurationObject);
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not disposal was invoked by user code.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified command.
+ ///
+ ///
+ /// Do not process using , as doing so will generally result in
+ /// infinite-looping; is exposed to this method to facilitate sub-command processing.
+ ///
+ ///
+ /// The command to process.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands. Do not process using
+ /// , as doing so will generally result in infinite-looping.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ ///
+ /// The result that is emitted when processing the command.
+ ///
+ protected sealed override TResult Process(TCommand command, ICommandMediator mediator, IConcurrencyControlToken controlToken) => command.Target switch
+ {
+ GetConfigurationObjectCommandTarget.ConnectionString => ConvertConfigurationObject(ApplicationConfiguration.GetConnectionString(command.Key)),
+ GetConfigurationObjectCommandTarget.Section => ConvertConfigurationObject(ApplicationConfiguration.GetSection(command.Key)),
+ GetConfigurationObjectCommandTarget.Value => ConvertConfigurationObject(ApplicationConfiguration.GetValue(command.Key)),
+ _ => throw new UnsupportedSpecificationException($"The specified configuration command target, {command.Target}, is not supported.")
+ };
+
+ ///
+ /// Represents configuration information for the application.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly IConfiguration ApplicationConfiguration;
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandTarget.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandTarget.cs
new file mode 100644
index 00000000..474bd266
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationObjectCommandTarget.cs
@@ -0,0 +1,40 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System;
+using System.Runtime.Serialization;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Specifies the target type of an .
+ ///
+ [DataContract]
+ public enum GetConfigurationObjectCommandTarget : Int32
+ {
+ ///
+ /// The target type is not specified.
+ ///
+ [EnumMember]
+ Unspecified = 0,
+
+ ///
+ /// The associated command targets a connection string with a specified key.
+ ///
+ [EnumMember]
+ ConnectionString = 1,
+
+ ///
+ /// The associated command targets a configuration section with a specified name.
+ ///
+ [EnumMember]
+ Section = 2,
+
+ ///
+ /// The associated command targets a configuration value with a specified key.
+ ///
+ [EnumMember]
+ Value = 3
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommand.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommand.cs
new file mode 100644
index 00000000..1724fa6b
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommand.cs
@@ -0,0 +1,69 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.Extensions.Configuration;
+using RapidField.SolidInstruments.Core;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using System;
+using System.Diagnostics;
+using System.Runtime.Serialization;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Represents a command that retrieves and deserializes a configuration section.
+ ///
+ [DataContract]
+ public sealed class GetConfigurationSectionCommand : GetConfigurationObjectCommand
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A textual key for the configuration section to retrieve.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ [DebuggerHidden]
+ internal GetConfigurationSectionCommand(String key)
+ : base(key, GetConfigurationObjectCommandTarget.Section)
+ {
+ return;
+ }
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration section.
+ ///
+ ///
+ /// The type of the configuration section.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration section to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the section was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static T Process(ICommandMediator mediator, String key)
+ where T : class => mediator.RejectIf().IsNull(nameof(mediator)).TargetArgument.Process(new GetConfigurationSectionCommand(key)).Get();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommandHandler.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommandHandler.cs
new file mode 100644
index 00000000..50bb5616
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationSectionCommandHandler.cs
@@ -0,0 +1,53 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.Extensions.Configuration;
+using System;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class GetConfigurationSectionCommandHandler : GetConfigurationObjectCommandHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public GetConfigurationSectionCommandHandler(ICommandMediator mediator, IConfiguration applicationConfiguration)
+ : base(mediator, applicationConfiguration)
+ {
+ return;
+ }
+
+ ///
+ /// Converts the specified configuration object to the appropriate result type.
+ ///
+ ///
+ /// The configuration value or section to convert.
+ ///
+ ///
+ /// The converted result object.
+ ///
+ protected override IConfigurationSection ConvertConfigurationObject(Object configurationObject) => configurationObject as IConfigurationSection;
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not disposal was invoked by user code.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommand.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommand.cs
new file mode 100644
index 00000000..fd44823f
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommand.cs
@@ -0,0 +1,350 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Core;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using System;
+using System.Diagnostics;
+using System.Runtime.Serialization;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Represents a command that retrieves and deserializes a configuration value.
+ ///
+ [DataContract]
+ public sealed class GetConfigurationValueCommand : GetConfigurationObjectCommand
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ [DebuggerHidden]
+ internal GetConfigurationValueCommand(String key)
+ : base(key, GetConfigurationObjectCommandTarget.Value)
+ {
+ return;
+ }
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Boolean ProcessBoolean(ICommandMediator mediator, String key) => Boolean.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static DateTime ProcessDateTime(ICommandMediator mediator, String key) => DateTime.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Decimal ProcessDecimal(ICommandMediator mediator, String key) => Decimal.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Double ProcessDouble(ICommandMediator mediator, String key) => Double.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Guid ProcessGuid(ICommandMediator mediator, String key) => Guid.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Int16 ProcessInt16(ICommandMediator mediator, String key) => Int16.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Int32 ProcessInt32(ICommandMediator mediator, String key) => Int32.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Int64 ProcessInt64(ICommandMediator mediator, String key) => Int64.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Single ProcessSingle(ICommandMediator mediator, String key) => Single.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static String ProcessString(ICommandMediator mediator, String key) => mediator.RejectIf().IsNull(nameof(mediator)).TargetArgument.Process(new GetConfigurationValueCommand(key));
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static TimeSpan ProcessTimeSpan(ICommandMediator mediator, String key) => TimeSpan.TryParse(ProcessString(mediator, key), out var result) ? result : default;
+
+ ///
+ /// Creates and processes a command that retrieves and deserializes a configuration value.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the configuration value to retrieve.
+ ///
+ ///
+ /// The resulting instance, or if the value was not resolved.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Uri ProcessUri(ICommandMediator mediator, String key) => Uri.TryCreate(ProcessString(mediator, key), UriKind.RelativeOrAbsolute, out var result) ? result : default;
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommandHandler.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommandHandler.cs
new file mode 100644
index 00000000..20d3834e
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConfigurationValueCommandHandler.cs
@@ -0,0 +1,53 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.Extensions.Configuration;
+using System;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class GetConfigurationValueCommandHandler : GetConfigurationObjectCommandHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public GetConfigurationValueCommandHandler(ICommandMediator mediator, IConfiguration applicationConfiguration)
+ : base(mediator, applicationConfiguration)
+ {
+ return;
+ }
+
+ ///
+ /// Converts the specified configuration object to the appropriate result type.
+ ///
+ ///
+ /// The configuration value or section to convert.
+ ///
+ ///
+ /// The converted result object.
+ ///
+ protected override String ConvertConfigurationObject(Object configurationObject) => configurationObject as String;
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not disposal was invoked by user code.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommand.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommand.cs
new file mode 100644
index 00000000..b0543126
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommand.cs
@@ -0,0 +1,64 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Core;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using System;
+using System.Diagnostics;
+using System.Runtime.Serialization;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Represents a command that retrieves a connection string.
+ ///
+ [DataContract]
+ public sealed class GetConnectionStringCommand : GetConfigurationObjectCommand
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A textual key for the connection string to retrieve.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ [DebuggerHidden]
+ internal GetConnectionStringCommand(String name)
+ : base(name, GetConfigurationObjectCommandTarget.ConnectionString)
+ {
+ return;
+ }
+
+ ///
+ /// Creates and processes a command that retrieves a connection string.
+ ///
+ ///
+ /// A processing intermediary that is used to process the command.
+ ///
+ ///
+ /// A textual key for the connection string to retrieve.
+ ///
+ ///
+ /// The resulting connection string, or
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static String Process(ICommandMediator mediator, String name) => mediator.RejectIf().IsNull(nameof(mediator)).TargetArgument.Process(new GetConnectionStringCommand(name));
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommandHandler.cs b/src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommandHandler.cs
new file mode 100644
index 00000000..084ff68f
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/GetConnectionStringCommandHandler.cs
@@ -0,0 +1,53 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.Extensions.Configuration;
+using System;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class GetConnectionStringCommandHandler : GetConfigurationObjectCommandHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public GetConnectionStringCommandHandler(ICommandMediator mediator, IConfiguration applicationConfiguration)
+ : base(mediator, applicationConfiguration)
+ {
+ return;
+ }
+
+ ///
+ /// Converts the specified configuration object to the appropriate result type.
+ ///
+ ///
+ /// The configuration value or section to convert.
+ ///
+ ///
+ /// The converted result object.
+ ///
+ protected override String ConvertConfigurationObject(Object configurationObject) => configurationObject as String;
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not disposal was invoked by user code.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Configuration/IGetConfigurationObjectCommand.cs b/src/RapidField.SolidInstruments.Command/Configuration/IGetConfigurationObjectCommand.cs
new file mode 100644
index 00000000..4d19dba2
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Configuration/IGetConfigurationObjectCommand.cs
@@ -0,0 +1,38 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System;
+using System.Runtime.Serialization;
+
+namespace RapidField.SolidInstruments.Command.Configuration
+{
+ ///
+ /// Represents a command that retrieves and deserializes a configuration value or section.
+ ///
+ ///
+ /// The type of the configuration object that is produced by interrogating the specified configuration key and target.
+ ///
+ public interface IGetConfigurationObjectCommand : ICommand
+ {
+ ///
+ /// Gets or sets a textual key for the configuration value or section to retrieve.
+ ///
+ [DataMember]
+ public String Key
+ {
+ get;
+ set;
+ }
+
+ ///
+ /// Gets or sets the target type of the current .
+ ///
+ [DataMember]
+ public GetConfigurationObjectCommandTarget Target
+ {
+ get;
+ set;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Extensions/ICommandMediatorExtensions.cs b/src/RapidField.SolidInstruments.Command/Extensions/ICommandMediatorExtensions.cs
new file mode 100644
index 00000000..a930a930
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Extensions/ICommandMediatorExtensions.cs
@@ -0,0 +1,200 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using System;
+using System.Threading.Tasks;
+
+namespace RapidField.SolidInstruments.Command.Extensions
+{
+ ///
+ /// Extends the interface with command handling features.
+ ///
+ public static class ICommandMediatorExtensions
+ {
+ ///
+ /// Processes the specified .
+ ///
+ ///
+ /// The type of the result that is produced by processing the command.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the command to process.
+ ///
+ ///
+ /// The result that is produced by processing the command.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static TResult Process(this ICommandMediator target, Func> createCommandFunction)
+ {
+ try
+ {
+ return target.Process(createCommandFunction.RejectIf().IsNull(nameof(createCommandFunction)).TargetArgument(CommandRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(exception);
+ }
+ }
+
+ ///
+ /// Processes the specified .
+ ///
+ ///
+ /// The type of the command to process.
+ ///
+ ///
+ /// The type of the result that is produced by processing the command.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the command to process.
+ ///
+ ///
+ /// The result that is produced by processing the command.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static TResult Process(this ICommandMediator target, Func createCommandFunction)
+ where TCommand : class, ICommand
+ {
+ try
+ {
+ return target.Process(createCommandFunction.RejectIf().IsNull(nameof(createCommandFunction)).TargetArgument(CommandRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TCommand), exception);
+ }
+ }
+
+ ///
+ /// Asynchronously processes the specified .
+ ///
+ ///
+ /// The type of the result that is produced by processing the command.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the command to process.
+ ///
+ ///
+ /// A task representing the asynchronous operation and containing the result that is produced by processing the command.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task ProcessAsync(this ICommandMediator target, Func> createCommandFunction)
+ {
+ try
+ {
+ return target.ProcessAsync(createCommandFunction.RejectIf().IsNull(nameof(createCommandFunction)).TargetArgument(CommandRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(exception);
+ }
+ }
+
+ ///
+ /// Asynchronously processes the specified .
+ ///
+ ///
+ /// The type of the command to process.
+ ///
+ ///
+ /// The type of the result that is produced by processing the command.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the command to process.
+ ///
+ ///
+ /// A task representing the asynchronous operation and containing the result that is produced by processing the command.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the command.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task ProcessAsync(this ICommandMediator target, Func createCommandFunction)
+ where TCommand : class, ICommand
+ {
+ try
+ {
+ return target.ProcessAsync(createCommandFunction.RejectIf().IsNull(nameof(createCommandFunction)).TargetArgument(CommandRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TCommand), exception);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/Extensions/ICommandRegisterExtensions.cs b/src/RapidField.SolidInstruments.Command/Extensions/ICommandRegisterExtensions.cs
new file mode 100644
index 00000000..7c33c204
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/Extensions/ICommandRegisterExtensions.cs
@@ -0,0 +1,114 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command.Configuration;
+using RapidField.SolidInstruments.Core;
+using System;
+using System.Collections.Generic;
+
+namespace RapidField.SolidInstruments.Command.Extensions
+{
+ ///
+ /// Extends the interface with general purpose command creation methods.
+ ///
+ public static class ICommandRegisterExtensions
+ {
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// The textual command value.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// A new .
+ ///
+ public static TextualCommand FromText(this ICommandRegister target, String value) => new TextualCommand(value);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// The textual command value.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the command.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// A new .
+ ///
+ public static TextualCommand FromText(this ICommandRegister target, String value, IEnumerable labels) => new TextualCommand(value, labels);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A textual key for the configuration section to retrieve.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ public static GetConfigurationSectionCommand GetConfigurationSection(this ICommandRegister target, String key) => new GetConfigurationSectionCommand(key);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A textual key for the configuration section to retrieve.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ public static GetConfigurationValueCommand GetConfigurationValue(this ICommandRegister target, String key) => new GetConfigurationValueCommand(key);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A textual key for the connection string to retrieve.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ public static GetConnectionStringCommand GetConnectionString(this ICommandRegister target, String name) => new GetConnectionStringCommand(name);
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/ICommandRegister.cs b/src/RapidField.SolidInstruments.Command/ICommandRegister.cs
new file mode 100644
index 00000000..c49b2aac
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Command/ICommandRegister.cs
@@ -0,0 +1,23 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command.Extensions;
+using System;
+
+namespace RapidField.SolidInstruments.Command
+{
+ ///
+ /// Represents an extensible catalog of available commands.
+ ///
+ ///
+ /// Consuming libraries may use as an extension method target to expose creational methods for
+ /// custom commands. See for examples. An instance of an
+ /// is made available by
+ /// and
+ /// .
+ ///
+ public interface ICommandRegister
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Command/RapidField.SolidInstruments.Command.csproj b/src/RapidField.SolidInstruments.Command/RapidField.SolidInstruments.Command.csproj
index ed6810df..ca48331f 100644
--- a/src/RapidField.SolidInstruments.Command/RapidField.SolidInstruments.Command.csproj
+++ b/src/RapidField.SolidInstruments.Command/RapidField.SolidInstruments.Command.csproj
@@ -38,6 +38,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
+
+
+
diff --git a/src/RapidField.SolidInstruments.Core/Caching/CacheAccessException.cs b/src/RapidField.SolidInstruments.Core/Caching/CacheAccessException.cs
new file mode 100644
index 00000000..d5cc9b13
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/CacheAccessException.cs
@@ -0,0 +1,85 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System;
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents an exception that is raised when a caching operation fails.
+ ///
+ public class CacheAccessException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CacheAccessException()
+ : this(cacheValueType: null)
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the
+ ///
+ ///
+ /// The type of the cache value that was being processed when the exception was raised.
+ ///
+ public CacheAccessException(Type cacheValueType)
+ : this(cacheValueType: cacheValueType, innerException: null)
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ public CacheAccessException(String message)
+ : this(message: message, innerException: null)
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the
+ ///
+ ///
+ /// The type of the cache value that was being processed when the exception was raised.
+ ///
+ ///
+ /// The exception that is the cause of the current exception.
+ ///
+ public CacheAccessException(Type cacheValueType, Exception innerException)
+ : this(cacheValueType is null ? "An exception was raised while accessing the cache or processing the cached object." : $"An exception was raised while processing a cached instance of type {cacheValueType.FullName}.", innerException)
+ {
+ CacheValueType = cacheValueType;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The error message that explains the reason for the exception.
+ ///
+ ///
+ /// The exception that is the cause of the current exception.
+ ///
+ public CacheAccessException(String message, Exception innerException)
+ : base(message, innerException)
+ {
+ return;
+ }
+
+ ///
+ /// Gets the type of the cache value that was being processed when the exception was raised.
+ ///
+ public Type CacheValueType
+ {
+ get;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/CacheClient.cs b/src/RapidField.SolidInstruments.Core/Caching/CacheClient.cs
new file mode 100644
index 00000000..5030714c
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/CacheClient.cs
@@ -0,0 +1,344 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using System;
+using System.Threading.Tasks;
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents a read-write client for accessing strongly-typed cached objects using textual keys.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public abstract class CacheClient : Instrument, ICacheClient
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected CacheClient()
+ : base()
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The concurrency control mode that is used to manage state. The default value is
+ /// .
+ ///
+ ///
+ /// is equal to .
+ ///
+ protected CacheClient(ConcurrencyControlMode stateControlMode)
+ : base(stateControlMode)
+ {
+ return;
+ }
+
+ ///
+ /// Removes the cached object using the specified textual key.
+ ///
+ ///
+ /// A textual key which uniquely identifies the cached object to remove.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public void Invalidate(String key)
+ {
+ using (var controlToken = StateControl.Enter())
+ {
+ RejectIfDisposed();
+ _ = key.RejectIf().IsNullOrEmpty(nameof(key));
+
+ try
+ {
+ Invalidate(key, controlToken);
+ }
+ catch (CacheAccessException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CacheAccessException("An exception was raised while invalidating the cached object.", exception);
+ }
+ }
+ }
+
+ ///
+ /// Attempts to retrieve the cached object using the specified textual key and, failing that, invokes the specified function
+ /// to produce the object and adds it to the cache.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies the object in the cache.
+ ///
+ ///
+ /// A function that produces the object if it is not found in the cache.
+ ///
+ ///
+ /// The resulting cached object.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache or produce the value.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public TValue Process(String key, Func produceValueFunction)
+ where TValue : class
+ {
+ _ = produceValueFunction.RejectIf().IsNull(nameof(produceValueFunction));
+
+ if (TryRead(key, out var value))
+ {
+ return value;
+ }
+
+ try
+ {
+ value = produceValueFunction();
+
+ if (value is not null)
+ {
+ Write(key, value);
+ }
+
+ return value;
+ }
+ catch (CacheAccessException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CacheAccessException(typeof(TValue), exception);
+ }
+ }
+
+ ///
+ /// Asynchronously attempts to retrieve the cached object using the specified textual key and, failing that, invokes the
+ /// specified function to produce the object and adds it to the cache.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies the object in the cache.
+ ///
+ ///
+ /// A function that produces the object if it is not found in the cache.
+ ///
+ ///
+ /// A task representing the asynchronous operation and containing the resulting cached object.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache or produce the value.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public Task ProcessAsync(String key, Func produceValueFunction)
+ where TValue : class => Task.Factory.StartNew(() => Process(key, produceValueFunction));
+
+ ///
+ /// Attempts to retrieve the cached object using the specified textual key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies in the cache.
+ ///
+ ///
+ /// The object to retrieve from the cache, or if the object was not successfully retrieved.
+ ///
+ ///
+ /// if the object was successfully retrieved, otherwise .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public Boolean TryRead(String key, out TValue value)
+ where TValue : class
+ {
+ value = null;
+ RejectIfDisposed();
+ _ = key.RejectIf().IsNullOrEmpty(nameof(key));
+
+ try
+ {
+ value = TryRead(key);
+ }
+ catch (CacheAccessException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CacheAccessException(typeof(TValue), exception);
+ }
+
+ return value is null ? false : true;
+ }
+
+ ///
+ /// Adds or updates the specified object using the specified key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies in the cache.
+ ///
+ ///
+ /// The object to add or update.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public void Write(String key, TValue value)
+ where TValue : class
+ {
+ using (var controlToken = StateControl.Enter())
+ {
+ RejectIfDisposed();
+ _ = key.RejectIf().IsNullOrEmpty(nameof(key));
+ _ = value.RejectIf().IsNull(nameof(value));
+
+ try
+ {
+ Write(key, value, controlToken);
+ }
+ catch (CacheAccessException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CacheAccessException(typeof(TValue), exception);
+ }
+ }
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not disposal was invoked by user code.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Removes the cached object using the specified textual key.
+ ///
+ ///
+ /// A textual key which uniquely identifies the cached object to remove.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ protected abstract void Invalidate(String key, IConcurrencyControlToken controlToken);
+
+ ///
+ /// Attempts to retrieve the cached object using the specified textual key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies a value in the cache.
+ ///
+ ///
+ /// The object retrieved from the cache, or if the object was not successfully retrieved.
+ ///
+ protected abstract TValue TryRead(String key)
+ where TValue : class;
+
+ ///
+ /// Adds or updates the specified object using the specified key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies in the cache.
+ ///
+ ///
+ /// The object to add or update.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected abstract void Write(String key, TValue value, IConcurrencyControlToken controlToken)
+ where TValue : class;
+
+ ///
+ /// Gets a value indicating whether or not the client is operative.
+ ///
+ public virtual Boolean IsOperative => true;
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/ICacheClient.cs b/src/RapidField.SolidInstruments.Core/Caching/ICacheClient.cs
new file mode 100644
index 00000000..68855ffa
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/ICacheClient.cs
@@ -0,0 +1,107 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System;
+using System.Threading.Tasks;
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents a read-write client for accessing strongly-typed cached objects using textual keys.
+ ///
+ public interface ICacheClient : ICacheReader, ICacheWriter
+ {
+ ///
+ /// Removes the cached object using the specified textual key.
+ ///
+ ///
+ /// A textual key which uniquely identifies the cached object to remove.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public void Invalidate(String key);
+
+ ///
+ /// Attempts to retrieve the cached object using the specified textual key and, failing that, invokes the specified function
+ /// to produce the object and adds it to the cache.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies the object in the cache.
+ ///
+ ///
+ /// A function that produces the object if it is not found in the cache.
+ ///
+ ///
+ /// The resulting cached object.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache or produce the value.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public TValue Process(String key, Func produceValueFunction)
+ where TValue : class;
+
+ ///
+ /// Asynchronously attempts to retrieve the cached object using the specified textual key and, failing that, invokes the
+ /// specified function to produce the object and adds it to the cache.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies the object in the cache.
+ ///
+ ///
+ /// A function that produces the object if it is not found in the cache.
+ ///
+ ///
+ /// A task representing the asynchronous operation and containing the resulting cached object.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache or produce the value.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public Task ProcessAsync(String key, Func produceValueFunction)
+ where TValue : class;
+
+ ///
+ /// Gets a value indicating whether or not the client is operative.
+ ///
+ public Boolean IsOperative
+ {
+ get;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/ICacheReader.cs b/src/RapidField.SolidInstruments.Core/Caching/ICacheReader.cs
new file mode 100644
index 00000000..f46127db
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/ICacheReader.cs
@@ -0,0 +1,44 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System;
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents a read-only client for accessing strongly-typed cached objects using textual keys.
+ ///
+ public interface ICacheReader : IDisposable
+ {
+ ///
+ /// Attempts to retrieve the cached object using the specified textual key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies in the cache.
+ ///
+ ///
+ /// The object to retrieve from the cache, or if the object was not successfully retrieved.
+ ///
+ ///
+ /// if the object was successfully retrieved, otherwise .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public Boolean TryRead(String key, out TValue value)
+ where TValue : class;
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/ICacheWriter.cs b/src/RapidField.SolidInstruments.Core/Caching/ICacheWriter.cs
new file mode 100644
index 00000000..53ff4508
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/ICacheWriter.cs
@@ -0,0 +1,41 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System;
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents a write-only client for accessing strongly-typed cached objects using textual keys.
+ ///
+ public interface ICacheWriter : IDisposable
+ {
+ ///
+ /// Adds or updates the specified object using the specified key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies in the cache.
+ ///
+ ///
+ /// The object to add or update.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public void Write(String key, TValue value)
+ where TValue : class;
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/IDistributedCacheClient.cs b/src/RapidField.SolidInstruments.Core/Caching/IDistributedCacheClient.cs
new file mode 100644
index 00000000..dbb88358
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/IDistributedCacheClient.cs
@@ -0,0 +1,13 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents a read-write client for accessing strongly-typed, remotely cached objects using textual keys.
+ ///
+ public interface IDistributedCacheClient : ICacheClient
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/IInMemoryCacheClient.cs b/src/RapidField.SolidInstruments.Core/Caching/IInMemoryCacheClient.cs
new file mode 100644
index 00000000..0f28f8a2
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/IInMemoryCacheClient.cs
@@ -0,0 +1,25 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System;
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents a read-write client for accessing strongly typed, locally cached objects using textual keys.
+ ///
+ public interface IInMemoryCacheClient : ICacheClient
+ {
+ ///
+ /// Removes all cached objects.
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public void Clear();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/InMemoryCacheClient.cs b/src/RapidField.SolidInstruments.Core/Caching/InMemoryCacheClient.cs
new file mode 100644
index 00000000..83b1bcd3
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/InMemoryCacheClient.cs
@@ -0,0 +1,243 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Options;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using RapidField.SolidInstruments.Core.Extensions;
+using System;
+using System.Diagnostics;
+using System.Threading;
+
+namespace RapidField.SolidInstruments.Core.Caching
+{
+ ///
+ /// Represents a read-write client for accessing strongly typed, locally cached objects using textual keys.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public sealed class InMemoryCacheClient : CacheClient, IInMemoryCacheClient
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public InMemoryCacheClient()
+ : this(DefaultStrategy)
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A value specifying the cache access and management behavior of the client. The default value is
+ /// .
+ ///
+ ///
+ /// is equal to .
+ ///
+ public InMemoryCacheClient(InMemoryCachingStrategy strategy)
+ : base(strategy == InMemoryCachingStrategy.NoCaching ? ConcurrencyControlMode.Unconstrained : ConcurrencyControlMode.ProcessorCountSemaphore)
+ {
+ Strategy = strategy.RejectIf().IsEqualToValue(InMemoryCachingStrategy.Unspecified, nameof(strategy));
+ LazyCache = IsOperative ? new Lazy(InitializeCache, LazyThreadSafetyMode.ExecutionAndPublication) : null;
+ LazyCacheOptions = IsOperative ? new Lazy>(InitializeCacheOptions, LazyThreadSafetyMode.ExecutionAndPublication) : null;
+ }
+
+ ///
+ /// Removes all cached objects.
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ public void Clear()
+ {
+ using (var controlToken = StateControl.Enter())
+ {
+ RejectIfDisposed();
+
+ try
+ {
+ if (IsOperative)
+ {
+ Cache?.Compact(1d);
+ }
+ }
+ catch (Exception exception)
+ {
+ throw new CacheAccessException("An exception was raised while clearing the cache.", exception);
+ }
+ }
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not disposal was invoked by user code.
+ ///
+ protected override void Dispose(Boolean disposing)
+ {
+ try
+ {
+ if (IsOperative)
+ {
+ LazyCache?.Dispose();
+ }
+ }
+ finally
+ {
+ base.Dispose(disposing);
+ }
+ }
+
+ ///
+ /// Removes the cached object using the specified textual key.
+ ///
+ ///
+ /// A textual key which uniquely identifies the cached object to remove.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while attempting to access the cache.
+ ///
+ ///
+ /// The object is disposed.
+ ///
+ protected override void Invalidate(String key, IConcurrencyControlToken controlToken)
+ {
+ if (IsOperative)
+ {
+ Cache?.Remove(key);
+ }
+ }
+
+ ///
+ /// Attempts to retrieve the cached object using the specified textual key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies a value in the cache.
+ ///
+ ///
+ /// The object retrieved from the cache, or if the object was not successfully retrieved.
+ ///
+ protected override TValue TryRead(String key)
+ where TValue : class
+ {
+ if (IsOperative)
+ {
+ return Cache.TryGetValue(key, out var value) ? value as TValue : null;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Adds or updates the specified object using the specified key.
+ ///
+ ///
+ /// The type of the cached object.
+ ///
+ ///
+ /// A textual key which uniquely identifies in the cache.
+ ///
+ ///
+ /// The object to add or update.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Write(String key, TValue value, IConcurrencyControlToken controlToken)
+ where TValue : class
+ {
+ if (IsOperative)
+ {
+ var options = Strategy.ToCacheEntryOptions();
+ options.Size = value.CalculateSizeInBytes();
+ _ = Cache?.Set(key, value, options);
+ }
+ }
+
+ ///
+ /// Initializes the underlying memory cache.
+ ///
+ ///
+ /// A new .
+ ///
+ [DebuggerHidden]
+ private MemoryCache InitializeCache() => new MemoryCache(CacheOptions);
+
+ ///
+ /// Initializes the configuration options for .
+ ///
+ ///
+ /// A new .
+ ///
+ [DebuggerHidden]
+ private IOptions InitializeCacheOptions() => Strategy.ToCacheOptions();
+
+ ///
+ /// Gets a value indicating whether or not the client is operative.
+ ///
+ public override Boolean IsOperative => Strategy != InMemoryCachingStrategy.NoCaching;
+
+ ///
+ /// Gets the underlying memory cache.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private MemoryCache Cache => LazyCache?.Value;
+
+ ///
+ /// Gets the configuration options for .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private IOptions CacheOptions => LazyCacheOptions?.Value;
+
+ ///
+ /// Represents a non-operative in-memory cache client.
+ ///
+ public static readonly IInMemoryCacheClient NonOperative = new InMemoryCacheClient(InMemoryCachingStrategy.NoCaching);
+
+ ///
+ /// Represents the default value specifying the cache access and management behavior of the client.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const InMemoryCachingStrategy DefaultStrategy = InMemoryCachingStrategy.Moderate;
+
+ ///
+ /// Represents the underlying, lazily-initialized memory cache.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly Lazy LazyCache;
+
+ ///
+ /// Represents lazily-initialized configuration options for .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly Lazy> LazyCacheOptions;
+
+ ///
+ /// Represents a value specifying the cache access and management behavior of the client.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly InMemoryCachingStrategy Strategy;
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Caching/InMemoryCachingStrategy.cs b/src/RapidField.SolidInstruments.Core/Caching/InMemoryCachingStrategy.cs
new file mode 100644
index 00000000..6f569d85
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Caching/InMemoryCachingStrategy.cs
@@ -0,0 +1,40 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Core.Caching;
+using System;
+
+namespace RapidField.SolidInstruments.Core
+{
+ ///
+ /// Specifies the cache access and management behavior of an .
+ ///
+ public enum InMemoryCachingStrategy : Int32
+ {
+ ///
+ /// The behavior of the in-memory cache client is not specified.
+ ///
+ Unspecified = 0,
+
+ ///
+ /// The client will not perform caching. This value can be used to create non-operative cache clients.
+ ///
+ NoCaching = 1,
+
+ ///
+ /// The client will perform lightweight caching, exercising minimal consumption of memory and processing resources.
+ ///
+ Conservative = 2,
+
+ ///
+ /// The client will perform caching exercising moderate consumption of memory and processing resources.
+ ///
+ Moderate = 3,
+
+ ///
+ /// The client will perform heavy caching, exercising high consumption of memory and processing resources.
+ ///
+ Aggressive = 4
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlOperationException.cs b/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlOperationException.cs
index e1eb161e..8e5a392d 100644
--- a/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlOperationException.cs
+++ b/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlOperationException.cs
@@ -56,7 +56,7 @@ public ConcurrencyControlOperationException(Exception innerException)
/// The exception that is the cause of the current exception.
///
public ConcurrencyControlOperationException(String message, Exception innerException)
- : base(message ?? ConstructMessage((innerException is null) == false), innerException)
+ : base(message ?? ConstructMessage(innerException is not null), innerException)
{
return;
}
diff --git a/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlToken.cs b/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlToken.cs
index 43a34835..929f60ef 100644
--- a/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlToken.cs
+++ b/src/RapidField.SolidInstruments.Core/Concurrency/ConcurrencyControlToken.cs
@@ -401,7 +401,7 @@ internal void Release()
}
catch (ConcurrencyControlOperationException)
{
- if ((Context is null) == false && Thread.CurrentThread != GranteeThread)
+ if (Context is not null && Thread.CurrentThread != GranteeThread)
{
// Attempt to avoid deadlocks in case the IConcurrencyControl implementation is using the wrong
// concurrency control primitive, or making some other unforeseen mistake.
diff --git a/src/RapidField.SolidInstruments.Core/Extensions/InMemoryCachingStrategyExtensions.cs b/src/RapidField.SolidInstruments.Core/Extensions/InMemoryCachingStrategyExtensions.cs
new file mode 100644
index 00000000..2979514d
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Core/Extensions/InMemoryCachingStrategyExtensions.cs
@@ -0,0 +1,317 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Options;
+using System;
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Core.Extensions
+{
+ ///
+ /// Extends the enumeration with general purpose features.
+ ///
+ public static class InMemoryCachingStrategyExtensions
+ {
+ ///
+ /// Converts the current to a memory cache entry options.
+ ///
+ ///
+ /// The current instance of the .
+ ///
+ ///
+ /// Memory cache entry options representing the current .
+ ///
+ [DebuggerHidden]
+ internal static MemoryCacheEntryOptions ToCacheEntryOptions(this InMemoryCachingStrategy target) => target switch
+ {
+ InMemoryCachingStrategy.Aggressive => CacheEntryOptionsForAggressiveStrategy,
+ InMemoryCachingStrategy.Conservative => CacheEntryOptionsForConservativeStrategy,
+ InMemoryCachingStrategy.Moderate => CacheEntryOptionsForModerateStrategy,
+ _ => throw new UnsupportedSpecificationException($"The specified caching strategy, {target}, is not supported."),
+ };
+
+ ///
+ /// Converts the current to a memory cache options.
+ ///
+ ///
+ /// The current instance of the .
+ ///
+ ///
+ /// Memory cache options representing the current .
+ ///
+ [DebuggerHidden]
+ internal static IOptions ToCacheOptions(this InMemoryCachingStrategy target) => target switch
+ {
+ InMemoryCachingStrategy.Aggressive => CacheOptionsForAggressiveStrategy,
+ InMemoryCachingStrategy.Conservative => CacheOptionsForConservativeStrategy,
+ InMemoryCachingStrategy.Moderate => CacheOptionsForModerateStrategy,
+ _ => throw new UnsupportedSpecificationException($"The specified caching strategy, {target}, is not supported."),
+ };
+
+ ///
+ /// Gets the amount of physical memory, in bytes, allocated for the current process.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static Int64 AvailableProcessMemoryInBytes => Process.GetCurrentProcess().WorkingSet64;
+
+ ///
+ /// Gets the settings associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static MemoryCacheEntryOptions CacheEntryOptionsForAggressiveStrategy => new MemoryCacheEntryOptions()
+ {
+ AbsoluteExpirationRelativeToNow = AbsoluteExpirationRelativeToNowForAggressiveStrategy,
+ SlidingExpiration = SlidingExpirationForAggressiveStrategy
+ };
+
+ ///
+ /// Gets the settings associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static MemoryCacheEntryOptions CacheEntryOptionsForConservativeStrategy => new MemoryCacheEntryOptions()
+ {
+ AbsoluteExpirationRelativeToNow = AbsoluteExpirationRelativeToNowForConservativeStrategy,
+ SlidingExpiration = SlidingExpirationForConservativeStrategy
+ };
+
+ ///
+ /// Gets the settings associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static MemoryCacheEntryOptions CacheEntryOptionsForModerateStrategy => new MemoryCacheEntryOptions()
+ {
+ AbsoluteExpirationRelativeToNow = AbsoluteExpirationRelativeToNowForModerateStrategy,
+ SlidingExpiration = SlidingExpirationForModerateStrategy
+ };
+
+ ///
+ /// Gets the settings associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static IOptions CacheOptionsForAggressiveStrategy => new MemoryCacheOptions()
+ {
+ CompactionPercentage = CompactionPercentageForAggressiveStrategy,
+ ExpirationScanFrequency = ExpirationScanFrequencyForAggressiveStrategy,
+ SizeLimit = SizeLimitForAggressiveStrategy
+ };
+
+ ///
+ /// Gets the settings associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static IOptions CacheOptionsForConservativeStrategy => new MemoryCacheOptions()
+ {
+ CompactionPercentage = CompactionPercentageForConservativeStrategy,
+ ExpirationScanFrequency = ExpirationScanFrequencyForConservativeStrategy,
+ SizeLimit = SizeLimitForConservativeStrategy
+ };
+
+ ///
+ /// Gets the settings associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static IOptions CacheOptionsForModerateStrategy => new MemoryCacheOptions()
+ {
+ CompactionPercentage = CompactionPercentageForModerateStrategy,
+ ExpirationScanFrequency = ExpirationScanFrequencyForModerateStrategy,
+ SizeLimit = SizeLimitForModerateStrategy
+ };
+
+ ///
+ /// Gets the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static Int64 SizeLimitForAggressiveStrategy => Convert.ToInt64(Math.Round(AvailableProcessMemoryInBytes * MemoryUsePercentageForAggressiveStrategy, 0));
+
+ ///
+ /// Gets the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static Int64 SizeLimitForConservativeStrategy => Convert.ToInt64(Math.Round(AvailableProcessMemoryInBytes * MemoryUsePercentageForConservativeStrategy, 0));
+
+ ///
+ /// Gets the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static Int64 SizeLimitForModerateStrategy => Convert.ToInt64(Math.Round(AvailableProcessMemoryInBytes * MemoryUsePercentageForModerateStrategy, 0));
+
+ ///
+ /// Represents the setting, in minutes, associated
+ /// with the strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 AbsoluteExpirationRelativeToNowInMinutesForAggressiveStrategy = 89;
+
+ ///
+ /// Represents the setting, in minutes, associated
+ /// with the strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 AbsoluteExpirationRelativeToNowInMinutesForConservativeStrategy = 13;
+
+ ///
+ /// Represents the setting, in minutes, associated
+ /// with the strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 AbsoluteExpirationRelativeToNowInMinutesForModerateStrategy = 34;
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Double CompactionPercentageForAggressiveStrategy = 0.08d;
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Double CompactionPercentageForConservativeStrategy = 0.21d;
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Double CompactionPercentageForModerateStrategy = 0.13d;
+
+ ///
+ /// Represents the setting, in minutes, associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 ExpirationScanFrequencyInMinutesForAggressiveStrategy = 1;
+
+ ///
+ /// Represents the setting, in minutes, associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 ExpirationScanFrequencyInMinutesForConservativeStrategy = 3;
+
+ ///
+ /// Represents the setting, in minutes, associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 ExpirationScanFrequencyInMinutesForModerateStrategy = 2;
+
+ ///
+ /// Represents the memory use percentage setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Double MemoryUsePercentageForAggressiveStrategy = 0.55d;
+
+ ///
+ /// Represents the memory use percentage setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Double MemoryUsePercentageForConservativeStrategy = 0.08d;
+
+ ///
+ /// Represents the memory use percentage setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Double MemoryUsePercentageForModerateStrategy = 0.21d;
+
+ ///
+ /// Represents the setting, in minutes, associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 SlidingExpirationInMinutesForAggressiveStrategy = 34;
+
+ ///
+ /// Represents the setting, in minutes, associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 SlidingExpirationInMinutesForConservativeStrategy = 5;
+
+ ///
+ /// Represents the setting, in minutes, associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 SlidingExpirationInMinutesForModerateStrategy = 13;
+
+ ///
+ /// Represents the setting associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan AbsoluteExpirationRelativeToNowForAggressiveStrategy = TimeSpan.FromMinutes(AbsoluteExpirationRelativeToNowInMinutesForAggressiveStrategy);
+
+ ///
+ /// Represents the setting associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan AbsoluteExpirationRelativeToNowForConservativeStrategy = TimeSpan.FromMinutes(AbsoluteExpirationRelativeToNowInMinutesForConservativeStrategy);
+
+ ///
+ /// Represents the setting associated with the
+ /// strategy .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan AbsoluteExpirationRelativeToNowForModerateStrategy = TimeSpan.FromMinutes(AbsoluteExpirationRelativeToNowInMinutesForModerateStrategy);
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan ExpirationScanFrequencyForAggressiveStrategy = TimeSpan.FromMinutes(ExpirationScanFrequencyInMinutesForAggressiveStrategy);
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan ExpirationScanFrequencyForConservativeStrategy = TimeSpan.FromMinutes(ExpirationScanFrequencyInMinutesForConservativeStrategy);
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan ExpirationScanFrequencyForModerateStrategy = TimeSpan.FromMinutes(ExpirationScanFrequencyInMinutesForModerateStrategy);
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan SlidingExpirationForAggressiveStrategy = TimeSpan.FromMinutes(SlidingExpirationInMinutesForAggressiveStrategy);
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan SlidingExpirationForConservativeStrategy = TimeSpan.FromMinutes(SlidingExpirationInMinutesForConservativeStrategy);
+
+ ///
+ /// Represents the setting associated with the strategy
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly TimeSpan SlidingExpirationForModerateStrategy = TimeSpan.FromMinutes(SlidingExpirationInMinutesForModerateStrategy);
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Core/Extensions/ObjectExtensions.cs b/src/RapidField.SolidInstruments.Core/Extensions/ObjectExtensions.cs
index a38f812d..67866162 100644
--- a/src/RapidField.SolidInstruments.Core/Extensions/ObjectExtensions.cs
+++ b/src/RapidField.SolidInstruments.Core/Extensions/ObjectExtensions.cs
@@ -3,8 +3,13 @@
// =================================================================================================================================
using System;
+using System.Collections;
+using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
@@ -15,6 +20,45 @@ namespace RapidField.SolidInstruments.Core.Extensions
///
public static class ObjectExtensions
{
+ ///
+ /// Reflectively interrogates the current object to determine its total size, in bytes, in memory.
+ ///
+ ///
+ /// The current instance of the .
+ ///
+ ///
+ /// The total size of the current object, in bytes.
+ ///
+ public static Int32 CalculateSizeInBytes(this Object target)
+ {
+ if (target?.GetType().IsValueType ?? false)
+ {
+ return Marshal.SizeOf(target);
+ }
+
+ var pointerSizeInBytes = IntPtr.Size;
+ var targetSizeInBytes = pointerSizeInBytes;
+
+ if (target is not null)
+ {
+ var values = new List
+
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Cryptography/Hashing/HashingProcessor.cs b/src/RapidField.SolidInstruments.Cryptography/Hashing/HashingProcessor.cs
index 9f0e8df1..7b48eecc 100644
--- a/src/RapidField.SolidInstruments.Cryptography/Hashing/HashingProcessor.cs
+++ b/src/RapidField.SolidInstruments.Cryptography/Hashing/HashingProcessor.cs
@@ -192,7 +192,7 @@ internal HashingProcessor()
///
public Byte[] CalculateHash(Byte[] plaintext, HashingAlgorithmSpecification algorithm, Byte[] salt)
{
- var applySalt = (salt is null) == false;
+ var applySalt = salt is not null;
var saltLengthInBytes = applySalt ? salt.Length : 0;
var plaintextLengthInBytes = plaintext.RejectIf().IsNullOrEmpty(nameof(plaintext)).TargetArgument.Length;
var saltedPlaintextLengthInBytes = plaintextLengthInBytes + saltLengthInBytes;
diff --git a/src/RapidField.SolidInstruments.Cryptography/Secrets/ExportedSecret.cs b/src/RapidField.SolidInstruments.Cryptography/Secrets/ExportedSecret.cs
index 723953f6..0d2c085e 100644
--- a/src/RapidField.SolidInstruments.Cryptography/Secrets/ExportedSecret.cs
+++ b/src/RapidField.SolidInstruments.Cryptography/Secrets/ExportedSecret.cs
@@ -135,7 +135,7 @@ private IReadOnlySecret HydrateSecret(Secret secret)
[DataMember]
public Boolean HasValue
{
- get => (Value is null) == false;
+ get => Value is not null;
set
{
if (value && Value is null)
diff --git a/src/RapidField.SolidInstruments.Cryptography/Secrets/SecretStoreFilePersistenceVehicle.cs b/src/RapidField.SolidInstruments.Cryptography/Secrets/SecretStoreFilePersistenceVehicle.cs
index b939b155..f186e822 100644
--- a/src/RapidField.SolidInstruments.Cryptography/Secrets/SecretStoreFilePersistenceVehicle.cs
+++ b/src/RapidField.SolidInstruments.Cryptography/Secrets/SecretStoreFilePersistenceVehicle.cs
@@ -105,7 +105,7 @@ protected override void Dispose(Boolean disposing)
{
try
{
- if (DeleteStateFileUponDisposal && (FilePath is null) == false && File.Exists(FilePath))
+ if (DeleteStateFileUponDisposal && FilePath is not null && File.Exists(FilePath))
{
File.Delete(FilePath);
}
diff --git a/src/RapidField.SolidInstruments.EventAuthoring/AssemblyAttributes.cs b/src/RapidField.SolidInstruments.EventAuthoring/AssemblyAttributes.cs
index a2b07fa8..40421483 100644
--- a/src/RapidField.SolidInstruments.EventAuthoring/AssemblyAttributes.cs
+++ b/src/RapidField.SolidInstruments.EventAuthoring/AssemblyAttributes.cs
@@ -5,4 +5,5 @@
using System.Runtime.CompilerServices;
[assembly: DisablePrivateReflection()]
-[assembly: InternalsVisibleTo("RapidField.SolidInstruments.EventAuthoring.UnitTests")]
\ No newline at end of file
+[assembly: InternalsVisibleTo("RapidField.SolidInstruments.EventAuthoring.UnitTests")]
+[assembly: InternalsVisibleTo("RapidField.SolidInstruments.Messaging")]
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.EventAuthoring/EventRegister.cs b/src/RapidField.SolidInstruments.EventAuthoring/EventRegister.cs
new file mode 100644
index 00000000..83ba58b5
--- /dev/null
+++ b/src/RapidField.SolidInstruments.EventAuthoring/EventRegister.cs
@@ -0,0 +1,32 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.EventAuthoring
+{
+ ///
+ /// Represents an extensible catalog of available events.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public sealed class EventRegister : IEventRegister
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [DebuggerHidden]
+ private EventRegister()
+ {
+ return;
+ }
+
+ ///
+ /// Represents a singleton instance of the class.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ internal static readonly IEventRegister Instance = new EventRegister();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.EventAuthoring/Extensions/ICommandMediatorExtensions.cs b/src/RapidField.SolidInstruments.EventAuthoring/Extensions/ICommandMediatorExtensions.cs
new file mode 100644
index 00000000..ec5a3399
--- /dev/null
+++ b/src/RapidField.SolidInstruments.EventAuthoring/Extensions/ICommandMediatorExtensions.cs
@@ -0,0 +1,183 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using System;
+using System.Threading.Tasks;
+
+namespace RapidField.SolidInstruments.EventAuthoring.Extensions
+{
+ ///
+ /// Extends the interface with event handling features.
+ ///
+ public static class ICommandMediatorExtensions
+ {
+ ///
+ /// Processes the specified .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the event to process.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the event.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static void HandleEvent(this ICommandMediator target, Func createEventFunction)
+ {
+ try
+ {
+ _ = target.Process(createEventFunction.RejectIf().IsNull(nameof(createEventFunction)).TargetArgument(EventRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(exception);
+ }
+ }
+
+ ///
+ /// Processes the specified .
+ ///
+ ///
+ /// The type of the event to process.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the event to process.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the event.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static void HandleEvent(this ICommandMediator target, Func createEventFunction)
+ where TEvent : class, IEvent
+ {
+ try
+ {
+ _ = target.Process(createEventFunction.RejectIf().IsNull(nameof(createEventFunction)).TargetArgument(EventRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TEvent), exception);
+ }
+ }
+
+ ///
+ /// Asynchronously processes the specified .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the event to process.
+ ///
+ ///
+ /// A task representing the asynchronous operation.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the event.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task HandleEventAsync(this ICommandMediator target, Func createEventFunction)
+ {
+ try
+ {
+ return target.ProcessAsync(createEventFunction.RejectIf().IsNull(nameof(createEventFunction)).TargetArgument(EventRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(exception);
+ }
+ }
+
+ ///
+ /// Asynchronously processes the specified .
+ ///
+ ///
+ /// The type of the event to process.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the event to process.
+ ///
+ ///
+ /// A task representing the asynchronous operation.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the event.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task HandleEventAsync(this ICommandMediator target, Func createEventFunction)
+ where TEvent : class, IEvent
+ {
+ try
+ {
+ return target.ProcessAsync(createEventFunction.RejectIf().IsNull(nameof(createEventFunction)).TargetArgument(EventRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TEvent), exception);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.EventAuthoring/Extensions/IEventRegisterExtensions.cs b/src/RapidField.SolidInstruments.EventAuthoring/Extensions/IEventRegisterExtensions.cs
new file mode 100644
index 00000000..b15849f4
--- /dev/null
+++ b/src/RapidField.SolidInstruments.EventAuthoring/Extensions/IEventRegisterExtensions.cs
@@ -0,0 +1,206 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Core;
+using System;
+using System.Collections.Generic;
+
+namespace RapidField.SolidInstruments.EventAuthoring.Extensions
+{
+ ///
+ /// Extends the interface with general purpose event creation methods.
+ ///
+ public static class IEventRegisterExtensions
+ {
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// An associated exception.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is .
+ ///
+ public static ErrorEvent Error(this IEventRegister target, Exception exception) => new ErrorEvent(exception);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// An associated exception.
+ ///
+ ///
+ /// A unique identifier that is assigned to related events.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// is equal to .
+ ///
+ public static ErrorEvent Error(this IEventRegister target, Exception exception, Guid correlationIdentifier) => new ErrorEvent(exception, correlationIdentifier);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A name or value that uniquely identifies the application in which the associated error occurred.
+ ///
+ ///
+ /// An associated exception.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public static ErrorEvent Error(this IEventRegister target, String applicationIdentity, Exception exception) => new ErrorEvent(applicationIdentity, exception);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A name or value that uniquely identifies the application in which the associated error occurred.
+ ///
+ ///
+ /// An associated exception.
+ ///
+ ///
+ /// A unique identifier that is assigned to related events.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ ///
+ /// is equal to .
+ ///
+ public static ErrorEvent Error(this IEventRegister target, String applicationIdentity, Exception exception, Guid correlationIdentifier) => new ErrorEvent(applicationIdentity, exception, correlationIdentifier);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is .
+ ///
+ public static GeneralInformationEvent GeneralInformation(this IEventRegister target, IEnumerable labels) => new GeneralInformationEvent(labels);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// The verbosity level of the event. The default value is
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// is equal to .
+ ///
+ public static GeneralInformationEvent GeneralInformation(this IEventRegister target, IEnumerable labels, EventVerbosity verbosity) => new GeneralInformationEvent(labels, verbosity);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// The verbosity level of the event. The default value is
+ ///
+ ///
+ /// A textual description of the event. This argument can be .
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// is equal to .
+ ///
+ public static GeneralInformationEvent GeneralInformation(this IEventRegister target, IEnumerable labels, EventVerbosity verbosity, String description) => new GeneralInformationEvent(labels, verbosity, description);
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// The verbosity level of the event. The default value is
+ ///
+ ///
+ /// A textual description of the event. This argument can be .
+ ///
+ ///
+ /// A unique identifier that is assigned to related events.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// is equal to -or-
+ /// is equal to .
+ ///
+ public static GeneralInformationEvent GeneralInformation(this IEventRegister target, IEnumerable labels, EventVerbosity verbosity, String description, Guid correlationIdentifier) => new GeneralInformationEvent(labels, verbosity, description, correlationIdentifier);
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.EventAuthoring/IEventRegister.cs b/src/RapidField.SolidInstruments.EventAuthoring/IEventRegister.cs
new file mode 100644
index 00000000..c47e7b48
--- /dev/null
+++ b/src/RapidField.SolidInstruments.EventAuthoring/IEventRegister.cs
@@ -0,0 +1,24 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.EventAuthoring.Extensions;
+using System;
+
+namespace RapidField.SolidInstruments.EventAuthoring
+{
+ ///
+ /// Represents an extensible catalog of available events.
+ ///
+ ///
+ /// Consuming libraries may use as an extension method target to expose creational methods for
+ /// custom events. See for examples. An instance of an is
+ /// made available by
+ /// and
+ /// .
+ ///
+ public interface IEventRegister
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.InversionOfControl.Autofac/AutofacServiceInjector.cs b/src/RapidField.SolidInstruments.InversionOfControl.Autofac/AutofacServiceInjector.cs
index 4032edda..ffb7c872 100644
--- a/src/RapidField.SolidInstruments.InversionOfControl.Autofac/AutofacServiceInjector.cs
+++ b/src/RapidField.SolidInstruments.InversionOfControl.Autofac/AutofacServiceInjector.cs
@@ -49,7 +49,7 @@ protected sealed override void Inject(ContainerBuilder configurator, IServiceCol
foreach (var serviceDescriptor in serviceDescriptors)
{
- if ((serviceDescriptor.ImplementationType is null) == false)
+ if (serviceDescriptor.ImplementationType is not null)
{
if (serviceDescriptor.ServiceType.GetTypeInfo().IsGenericTypeDefinition)
{
@@ -59,7 +59,7 @@ protected sealed override void Inject(ContainerBuilder configurator, IServiceCol
configurator.RegisterType(serviceDescriptor.ImplementationType).As(serviceDescriptor.ServiceType).WithLifetime(serviceDescriptor.Lifetime);
}
- else if ((serviceDescriptor.ImplementationFactory is null) == false)
+ else if (serviceDescriptor.ImplementationFactory is not null)
{
configurator.RegisterComponent(RegistrationBuilder.ForDelegate(serviceDescriptor.ServiceType, (context, parameters) => serviceDescriptor.ImplementationFactory(context.Resolve())).WithLifetime(serviceDescriptor.Lifetime).CreateRegistration());
}
diff --git a/src/RapidField.SolidInstruments.InversionOfControl/DependencyEngine.cs b/src/RapidField.SolidInstruments.InversionOfControl/DependencyEngine.cs
index cf974fd6..88674197 100644
--- a/src/RapidField.SolidInstruments.InversionOfControl/DependencyEngine.cs
+++ b/src/RapidField.SolidInstruments.InversionOfControl/DependencyEngine.cs
@@ -414,7 +414,7 @@ internal static TEngine New(IConfiguration app
var engine = package.CreateEngine(applicationConfiguration.RejectIf().IsNull(nameof(applicationConfiguration)).TargetArgument, serviceDescriptors);
var abstractEngine = engine as DependencyEngine;
- if ((abstractEngine is null) == false)
+ if (abstractEngine is not null)
{
abstractEngine.Start();
}
diff --git a/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/Extensions/ContainerBuilderExtensions.cs b/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/Extensions/ContainerBuilderExtensions.cs
index 21df5488..662e7569 100644
--- a/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/Extensions/ContainerBuilderExtensions.cs
+++ b/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/Extensions/ContainerBuilderExtensions.cs
@@ -5,6 +5,7 @@
using Autofac;
using Microsoft.Azure.ServiceBus;
using Microsoft.Extensions.Configuration;
+using RapidField.SolidInstruments.Command.Autofac.Extensions;
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using RapidField.SolidInstruments.Core.Extensions;
@@ -43,6 +44,7 @@ public static class ContainerBuilderExtensions
public static void RegisterSupportingTypesForAzureServiceBusMessaging(this ContainerBuilder target, IConfiguration applicationConfiguration, String connectionStringConfigurationKeyName)
{
target.RegisterApplicationConfiguration(applicationConfiguration);
+ target.RegisterConfigurationCommandHandlers();
_ = connectionStringConfigurationKeyName.RejectIf().IsNullOrEmpty(nameof(connectionStringConfigurationKeyName));
target.Register(context =>
diff --git a/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/Extensions/ContainerBuilderExtensions.cs b/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/Extensions/ContainerBuilderExtensions.cs
index ee63f715..efd80ac5 100644
--- a/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/Extensions/ContainerBuilderExtensions.cs
+++ b/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/Extensions/ContainerBuilderExtensions.cs
@@ -4,6 +4,7 @@
using Autofac;
using Microsoft.Extensions.Configuration;
+using RapidField.SolidInstruments.Command.Autofac.Extensions;
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using RapidField.SolidInstruments.Core.Extensions;
@@ -220,6 +221,7 @@ public static void RegisterSupportingTypesForRabbitMqMessaging(this ContainerBui
private static void RegisterSupportingTypesForRabbitMqMessaging(this ContainerBuilder target, IConfiguration applicationConfiguration, RabbitMqMessageTransport transport)
{
target.RegisterApplicationConfiguration(applicationConfiguration);
+ target.RegisterConfigurationCommandHandlers();
target.RegisterInstance(transport).IfNotRegistered(typeof(RabbitMqMessageTransport)).AsSelf().SingleInstance();
target.RegisterInstance(transport.CreateConnection()).IfNotRegistered(typeof(IMessageTransportConnection)).AsSelf().SingleInstance();
target.RegisterType().IfNotRegistered(typeof(RabbitMqMessageAdapter)).As>().AsSelf().SingleInstance();
diff --git a/src/RapidField.SolidInstruments.Messaging.AzureServiceBus/AzureServiceBusListeningFacade.cs b/src/RapidField.SolidInstruments.Messaging.AzureServiceBus/AzureServiceBusListeningFacade.cs
index d7a18256..cc2b997a 100644
--- a/src/RapidField.SolidInstruments.Messaging.AzureServiceBus/AzureServiceBusListeningFacade.cs
+++ b/src/RapidField.SolidInstruments.Messaging.AzureServiceBus/AzureServiceBusListeningFacade.cs
@@ -161,7 +161,7 @@ protected sealed override Task RouteToDeadLetterQueueAsync(AzureServic
[DebuggerHidden]
private static Guid ExtractCorrelationIdentifier(AzureServiceBusMessage message)
{
- if ((message?.CorrelationId is null) == false && Guid.TryParse(message.CorrelationId, out var correlationIdentifier))
+ if (message?.CorrelationId is not null && Guid.TryParse(message.CorrelationId, out var correlationIdentifier))
{
return correlationIdentifier;
}
diff --git a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/Extensions/IServiceCollectionExtensions.cs b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/Extensions/IServiceCollectionExtensions.cs
index f68f4d6a..5362c1a1 100644
--- a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/Extensions/IServiceCollectionExtensions.cs
+++ b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/Extensions/IServiceCollectionExtensions.cs
@@ -7,6 +7,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using RapidField.SolidInstruments.Command.DotNetNative.Extensions;
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using RapidField.SolidInstruments.Core.Extensions;
@@ -49,6 +50,7 @@ public static class IServiceCollectionExtensions
public static IServiceCollection AddSupportingTypesForAzureServiceBusMessaging(this IServiceCollection target, IConfiguration applicationConfiguration, String connectionStringConfigurationKeyName)
{
target.AddApplicationConfiguration(applicationConfiguration);
+ target.AddConfigurationCommandHandlers();
_ = connectionStringConfigurationKeyName.RejectIf().IsNullOrEmpty(nameof(connectionStringConfigurationKeyName));
target.TryAddSingleton((serviceProvider) =>
diff --git a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/Extensions/IServiceCollectionExtensions.cs b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/Extensions/IServiceCollectionExtensions.cs
index 2b432e84..e581927a 100644
--- a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/Extensions/IServiceCollectionExtensions.cs
+++ b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/Extensions/IServiceCollectionExtensions.cs
@@ -5,6 +5,7 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using RapidField.SolidInstruments.Command.DotNetNative.Extensions;
using RapidField.SolidInstruments.Core;
using RapidField.SolidInstruments.Core.ArgumentValidation;
using RapidField.SolidInstruments.Core.Extensions;
@@ -243,6 +244,7 @@ public static IServiceCollection AddSupportingTypesForRabbitMqMessaging(this ISe
private static IServiceCollection AddSupportingTypesForRabbitMqMessaging(this IServiceCollection target, IConfiguration applicationConfiguration, RabbitMqMessageTransport transport)
{
target.AddApplicationConfiguration(applicationConfiguration);
+ target.AddConfigurationCommandHandlers();
target.TryAddSingleton(transport);
target.TryAddSingleton(transport.CreateConnection());
target.TryAddSingleton();
diff --git a/src/RapidField.SolidInstruments.Messaging/CommandMessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/CommandMessageRegister.cs
new file mode 100644
index 00000000..b27cc2be
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/CommandMessageRegister.cs
@@ -0,0 +1,41 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available command messages.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public sealed class CommandMessageRegister : ICommandMessageRegister
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [DebuggerHidden]
+ private CommandMessageRegister()
+ {
+ Commands = CommandRegister.Instance;
+ }
+
+ ///
+ /// Gets a catalog of available commands.
+ ///
+ public ICommandRegister Commands
+ {
+ get;
+ }
+
+ ///
+ /// Represents a singleton instance of the class.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ internal static readonly ICommandMessageRegister Instance = new CommandMessageRegister();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/CommandMessages/TextualCommandMessage.cs b/src/RapidField.SolidInstruments.Messaging/CommandMessages/TextualCommandMessage.cs
new file mode 100644
index 00000000..87dcda79
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/CommandMessages/TextualCommandMessage.cs
@@ -0,0 +1,41 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using System;
+using System.Runtime.Serialization;
+
+namespace RapidField.SolidInstruments.Messaging.CommandMessages
+{
+ ///
+ /// Represents a message that contains a command that is described by textual information.
+ ///
+ [DataContract]
+ public sealed class TextualCommandMessage : CommandMessage
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public TextualCommandMessage()
+ : base()
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The associated command.
+ ///
+ ///
+ /// is .
+ ///
+ public TextualCommandMessage(TextualCommand commandObject)
+ : base(commandObject)
+ {
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/EventMessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/EventMessageRegister.cs
new file mode 100644
index 00000000..255bb7fc
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/EventMessageRegister.cs
@@ -0,0 +1,41 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.EventAuthoring;
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available event messages.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public sealed class EventMessageRegister : IEventMessageRegister
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [DebuggerHidden]
+ private EventMessageRegister()
+ {
+ Events = EventRegister.Instance;
+ }
+
+ ///
+ /// Gets a catalog of available events.
+ ///
+ public IEventRegister Events
+ {
+ get;
+ }
+
+ ///
+ /// Represents a singleton instance of the class.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ internal static readonly IEventMessageRegister Instance = new EventMessageRegister();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMediatorExtensions.cs b/src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMediatorExtensions.cs
new file mode 100644
index 00000000..d4f5cd3e
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMediatorExtensions.cs
@@ -0,0 +1,479 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Messaging.CommandMessages;
+using RapidField.SolidInstruments.Messaging.EventMessages;
+using System;
+using System.Threading.Tasks;
+
+namespace RapidField.SolidInstruments.Messaging.Extensions
+{
+ ///
+ /// Extends the interface with message handling features.
+ ///
+ public static class ICommandMediatorExtensions
+ {
+ ///
+ /// Transmits the specified .
+ ///
+ ///
+ /// The type of the command message to transmit.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the command message to transmit.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static void TransmitCommandMessage(this ICommandMediator target, Func createMessageFunction)
+ where TCommandMessage : class, ICommandMessage
+ {
+ try
+ {
+ _ = target.Process(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(CommandMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TCommandMessage), exception);
+ }
+ }
+
+ ///
+ /// Asynchronously transmits the specified .
+ ///
+ ///
+ /// The type of the command message to transmit.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the command message to transmit.
+ ///
+ ///
+ /// A task representing the asynchronous operation.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task TransmitCommandMessageAsync(this ICommandMediator target, Func createMessageFunction)
+ where TCommandMessage : class, ICommandMessage
+ {
+ try
+ {
+ return target.ProcessAsync(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(CommandMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TCommandMessage), exception);
+ }
+ }
+
+ ///
+ /// Transmits the specified .
+ ///
+ ///
+ /// The type of the event message to transmit.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the event message to transmit.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static void TransmitEventMessage(this ICommandMediator target, Func createMessageFunction)
+ where TEventMessage : class, IMessage, IEventMessageBase
+ {
+ try
+ {
+ _ = target.Process(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(EventMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TEventMessage), exception);
+ }
+ }
+
+ ///
+ /// Asynchronously transmits the specified .
+ ///
+ ///
+ /// The type of the event message to transmit.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the event message to transmit.
+ ///
+ ///
+ /// A task representing the asynchronous operation.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task TransmitEventMessageAsync(this ICommandMediator target, Func createMessageFunction)
+ where TEventMessage : class, IMessage, IEventMessageBase
+ {
+ try
+ {
+ return target.ProcessAsync(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(EventMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TEventMessage), exception);
+ }
+ }
+
+ ///
+ /// Transmits the specified .
+ ///
+ ///
+ /// The type of the message to transmit.
+ ///
+ ///
+ /// The type of the result that is produced by processing the message.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the message to transmit.
+ ///
+ ///
+ /// The result that is produced by processing the message.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static TResult TransmitMessage(this ICommandMediator target, Func createMessageFunction)
+ where TMessage : class, IMessage
+ {
+ try
+ {
+ return target.Process(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(MessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TMessage), exception);
+ }
+ }
+
+ ///
+ /// Asynchronously transmits the specified .
+ ///
+ ///
+ /// The type of the message to transmit.
+ ///
+ ///
+ /// The type of the result that is produced by processing the message.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the message to transmit.
+ ///
+ ///
+ /// A task representing the asynchronous operation and containing the result that is produced by processing the message.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task TransmitMessageAsync(this ICommandMediator target, Func createMessageFunction)
+ where TMessage : class, IMessage
+ {
+ try
+ {
+ return target.ProcessAsync(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(MessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TMessage), exception);
+ }
+ }
+
+ ///
+ /// Transmits the specified .
+ ///
+ ///
+ /// The type of the response message that is produced by processing the request message.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the request message to transmit.
+ ///
+ ///
+ /// The response message that is produced by processing the request message.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static TResponseMessage TransmitRequestMessage(this ICommandMediator target, Func> createMessageFunction)
+ where TResponseMessage : class, IResponseMessage
+ {
+ try
+ {
+ return target.Process(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(RequestMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(exception);
+ }
+ }
+
+ ///
+ /// Transmits the specified .
+ ///
+ ///
+ /// The type of the request message to transmit.
+ ///
+ ///
+ /// The type of the response message that is produced by processing the request message.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the request message to transmit.
+ ///
+ ///
+ /// The response message that is produced by processing the request message.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static TResponseMessage TransmitRequestMessage(this ICommandMediator target, Func createMessageFunction)
+ where TRequestMessage : class, IRequestMessage
+ where TResponseMessage : class, IResponseMessage
+ {
+ try
+ {
+ return target.Process(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(RequestMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TRequestMessage), exception);
+ }
+ }
+
+ ///
+ /// Asynchronously transmits the specified .
+ ///
+ ///
+ /// The type of the response message that is produced by processing the request message.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the request message to transmit.
+ ///
+ ///
+ /// A task representing the asynchronous operation and containing the response message that is produced by processing the
+ /// request message.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task TransmitRequestMessageAsync(this ICommandMediator target, Func> createMessageFunction)
+ where TResponseMessage : class, IResponseMessage
+ {
+ try
+ {
+ return target.ProcessAsync(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(RequestMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(exception);
+ }
+ }
+
+ ///
+ /// Asynchronously transmits the specified .
+ ///
+ ///
+ /// The type of the request message to transmit.
+ ///
+ ///
+ /// The type of the response message that is produced by processing the request message.
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A function that returns the request message to transmit.
+ ///
+ ///
+ /// A task representing the asynchronous operation and containing the response message that is produced by processing the
+ /// request message.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// An exception was raised while processing the message.
+ ///
+ ///
+ /// is disposed.
+ ///
+ public static Task TransmitRequestMessageAsync(this ICommandMediator target, Func createMessageFunction)
+ where TRequestMessage : class, IRequestMessage
+ where TResponseMessage : class, IResponseMessage
+ {
+ try
+ {
+ return target.ProcessAsync(createMessageFunction.RejectIf().IsNull(nameof(createMessageFunction)).TargetArgument(RequestMessageRegister.Instance));
+ }
+ catch (CommandHandlingException)
+ {
+ throw;
+ }
+ catch (ObjectDisposedException)
+ {
+ throw;
+ }
+ catch (Exception exception)
+ {
+ throw new CommandHandlingException(typeof(TRequestMessage), exception);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMessageRegisterExtensions.cs b/src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMessageRegisterExtensions.cs
new file mode 100644
index 00000000..75cb49bc
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/Extensions/ICommandMessageRegisterExtensions.cs
@@ -0,0 +1,54 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command.Extensions;
+using RapidField.SolidInstruments.Messaging.CommandMessages;
+using System;
+using System.Collections.Generic;
+
+namespace RapidField.SolidInstruments.Messaging.Extensions
+{
+ ///
+ /// Extends the interface with general purpose message creation methods.
+ ///
+ public static class ICommandMessageRegisterExtensions
+ {
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// The textual command value.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// A new .
+ ///
+ public static TextualCommandMessage FromText(this ICommandMessageRegister target, String value) => new TextualCommandMessage(target.Commands.FromText(value));
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// The textual command value.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the command.
+ ///
+ ///
+ /// is -or- is .
+ ///
+ ///
+ /// A new .
+ ///
+ public static TextualCommandMessage FromText(this ICommandMessageRegister target, String value, IEnumerable labels) => new TextualCommandMessage(target.Commands.FromText(value, labels));
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/Extensions/IEventMessageRegisterExtensions.cs b/src/RapidField.SolidInstruments.Messaging/Extensions/IEventMessageRegisterExtensions.cs
new file mode 100644
index 00000000..c014ba6a
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/Extensions/IEventMessageRegisterExtensions.cs
@@ -0,0 +1,13 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+namespace RapidField.SolidInstruments.Messaging.Extensions
+{
+ ///
+ /// Extends the interface with general purpose message creation methods.
+ ///
+ public static class IEventMessageRegisterExtensions
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/Extensions/IMessageRegisterExtensions.cs b/src/RapidField.SolidInstruments.Messaging/Extensions/IMessageRegisterExtensions.cs
new file mode 100644
index 00000000..2bf88718
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/Extensions/IMessageRegisterExtensions.cs
@@ -0,0 +1,13 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+namespace RapidField.SolidInstruments.Messaging.Extensions
+{
+ ///
+ /// Extends the interface with general purpose message creation methods.
+ ///
+ public static class IMessageRegisterExtensions
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/Extensions/IRequestMessageRegisterExtensions.cs b/src/RapidField.SolidInstruments.Messaging/Extensions/IRequestMessageRegisterExtensions.cs
new file mode 100644
index 00000000..75cebb3e
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/Extensions/IRequestMessageRegisterExtensions.cs
@@ -0,0 +1,43 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Messaging.Service;
+using System;
+
+namespace RapidField.SolidInstruments.Messaging.Extensions
+{
+ ///
+ /// Extends the interface with general purpose message creation methods.
+ ///
+ public static class IRequestMessageRegisterExtensions
+ {
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A new .
+ ///
+ public static PingRequestMessage Ping(this IRequestMessageRegister target) => target.Ping(Guid.NewGuid());
+
+ ///
+ /// Creates a new .
+ ///
+ ///
+ /// The current .
+ ///
+ ///
+ /// A unique identifier that is assigned to related messages.
+ ///
+ ///
+ /// A new .
+ ///
+ ///
+ /// is equal to .
+ ///
+ public static PingRequestMessage Ping(this IRequestMessageRegister target, Guid correlationIdentifier) => new PingRequestMessage(correlationIdentifier);
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/ICommandMessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/ICommandMessageRegister.cs
new file mode 100644
index 00000000..38d28205
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/ICommandMessageRegister.cs
@@ -0,0 +1,32 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Messaging.Extensions;
+using System;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available command messages.
+ ///
+ ///
+ /// Consuming libraries may use as an extension method target to expose creational
+ /// methods for custom messages. See for examples. An instance of an
+ /// is made available by
+ ///
+ /// and
+ /// .
+ ///
+ public interface ICommandMessageRegister
+ {
+ ///
+ /// Gets a catalog of available commands.
+ ///
+ public ICommandRegister Commands
+ {
+ get;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/IEventMessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/IEventMessageRegister.cs
new file mode 100644
index 00000000..95a74b76
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/IEventMessageRegister.cs
@@ -0,0 +1,33 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.EventAuthoring;
+using RapidField.SolidInstruments.Messaging.Extensions;
+using System;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available event messages.
+ ///
+ ///
+ /// Consuming libraries may use as an extension method target to expose creational methods
+ /// for custom messages. See for examples. An instance of an
+ /// is made available by
+ ///
+ /// and
+ /// .
+ ///
+ public interface IEventMessageRegister
+ {
+ ///
+ /// Gets a catalog of available events.
+ ///
+ public IEventRegister Events
+ {
+ get;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/IMessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/IMessageRegister.cs
new file mode 100644
index 00000000..fe98bb5b
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/IMessageRegister.cs
@@ -0,0 +1,48 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Messaging.Extensions;
+using System;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available messages.
+ ///
+ ///
+ /// Consuming libraries may use as an extension method target to expose creational methods for
+ /// custom messages. See for examples. An instance of an
+ /// is made available by
+ ///
+ /// and
+ /// .
+ ///
+ public interface IMessageRegister
+ {
+ ///
+ /// Gets a catalog of available command messages.
+ ///
+ public ICommandMessageRegister CommandMessages
+ {
+ get;
+ }
+
+ ///
+ /// Gets a catalog of available event messages.
+ ///
+ public IEventMessageRegister EventMessages
+ {
+ get;
+ }
+
+ ///
+ /// Gets a catalog of available request messages.
+ ///
+ public IRequestMessageRegister RequestMessages
+ {
+ get;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/IRequestMessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/IRequestMessageRegister.cs
new file mode 100644
index 00000000..775b01ad
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/IRequestMessageRegister.cs
@@ -0,0 +1,25 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Messaging.Extensions;
+using System;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available request messages.
+ ///
+ ///
+ /// Consuming libraries may use as an extension method target to expose creational
+ /// methods for custom messages. See for examples. An instance of an
+ /// is made available by
+ ///
+ /// and
+ /// .
+ ///
+ public interface IRequestMessageRegister
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/MessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/MessageRegister.cs
new file mode 100644
index 00000000..3cae2bda
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/MessageRegister.cs
@@ -0,0 +1,58 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available messages.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public sealed class MessageRegister : IMessageRegister
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [DebuggerHidden]
+ private MessageRegister()
+ {
+ CommandMessages = CommandMessageRegister.Instance;
+ EventMessages = EventMessageRegister.Instance;
+ RequestMessages = RequestMessageRegister.Instance;
+ }
+
+ ///
+ /// Gets a catalog of available command messages.
+ ///
+ public ICommandMessageRegister CommandMessages
+ {
+ get;
+ }
+
+ ///
+ /// Gets a catalog of available event messages.
+ ///
+ public IEventMessageRegister EventMessages
+ {
+ get;
+ }
+
+ ///
+ /// Gets a catalog of available request messages.
+ ///
+ public IRequestMessageRegister RequestMessages
+ {
+ get;
+ }
+
+ ///
+ /// Represents a singleton instance of the class.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ internal static readonly IMessageRegister Instance = new MessageRegister();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/RequestMessageRegister.cs b/src/RapidField.SolidInstruments.Messaging/RequestMessageRegister.cs
new file mode 100644
index 00000000..55396173
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Messaging/RequestMessageRegister.cs
@@ -0,0 +1,32 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Messaging
+{
+ ///
+ /// Represents an extensible catalog of available request messages.
+ ///
+ ///
+ /// is the default implementation of .
+ ///
+ public sealed class RequestMessageRegister : IRequestMessageRegister
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ [DebuggerHidden]
+ private RequestMessageRegister()
+ {
+ return;
+ }
+
+ ///
+ /// Represents a singleton instance of the class.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ internal static readonly IRequestMessageRegister Instance = new RequestMessageRegister();
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Messaging/TransportPrimitives/PrimitiveMessage.cs b/src/RapidField.SolidInstruments.Messaging/TransportPrimitives/PrimitiveMessage.cs
index 8de1e819..a0fd96b1 100644
--- a/src/RapidField.SolidInstruments.Messaging/TransportPrimitives/PrimitiveMessage.cs
+++ b/src/RapidField.SolidInstruments.Messaging/TransportPrimitives/PrimitiveMessage.cs
@@ -209,7 +209,7 @@ internal MessageLockToken LockToken
///
/// An error occurred during serialization or deserialization.
///
- [DataMember(Name = "Body", Order = 3)]
+ [DataMember(Name = SerializedBodyDataMemberName, Order = 3)]
internal String SerializedBody
{
get => SerializeBody();
@@ -251,6 +251,12 @@ private Type BodyType
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
internal const SerializationFormat DefaultBodySerializationFormat = SerializationFormat.CompressedJson;
+ ///
+ /// Represents the name of in serialization contexts.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String SerializedBodyDataMemberName = "Body";
+
///
/// Represents the text encoding that is used to encode and decode .
///
diff --git a/src/RapidField.SolidInstruments.SignalProcessing/Channel.cs b/src/RapidField.SolidInstruments.SignalProcessing/Channel.cs
index e0942ae9..7cdc7b5c 100644
--- a/src/RapidField.SolidInstruments.SignalProcessing/Channel.cs
+++ b/src/RapidField.SolidInstruments.SignalProcessing/Channel.cs
@@ -324,7 +324,7 @@ public Task> ReadAsync(Int32 index, Int32 lookBehindLength, Int
argumentOutOfRangeExceptionParameterName = nameof(lookAheadLength);
}
- if ((argumentOutOfRangeExceptionParameterName is null) == false)
+ if (argumentOutOfRangeExceptionParameterName is not null)
{
return InvalidReadBehavior switch
{
diff --git a/src/RapidField.SolidInstruments.Web/DomainModelHttpApiController.cs b/src/RapidField.SolidInstruments.Web/DomainModelHttpApiController.cs
new file mode 100644
index 00000000..da401e87
--- /dev/null
+++ b/src/RapidField.SolidInstruments.Web/DomainModelHttpApiController.cs
@@ -0,0 +1,40 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.Domain;
+using System;
+
+namespace RapidField.SolidInstruments.Web
+{
+ ///
+ /// Processes HTTP requests representing domain model CRUD operations for an API endpoint.
+ ///
+ ///
+ /// The type of the unique primary identifier for the associated model type.
+ ///
+ ///
+ /// The type of the domain model for which the controller processes CRUD operations.
+ ///
+ public abstract class DomainModelHttpApiController : HttpApiController
+ where TDomainModelIdentifier : IComparable, IComparable, IEquatable
+ where TDomainModel : class, IDomainModel
+ {
+ ///
+ /// Initializes a new instance of the
+ /// class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// is .
+ ///
+ protected DomainModelHttpApiController(ICommandMediator mediator)
+ : base(mediator)
+ {
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Web/HttpApiController.cs b/src/RapidField.SolidInstruments.Web/HttpApiController.cs
index 0f3a4f37..4fac406f 100644
--- a/src/RapidField.SolidInstruments.Web/HttpApiController.cs
+++ b/src/RapidField.SolidInstruments.Web/HttpApiController.cs
@@ -2,12 +2,155 @@
// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
// =================================================================================================================================
+using Microsoft.AspNetCore.Mvc;
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Extensions;
+using System;
+using System.Diagnostics;
+using System.Net;
+
namespace RapidField.SolidInstruments.Web
{
///
/// Processes HTTP requests for an API endpoint.
///
- public abstract class HttpApiController
+ public abstract class HttpApiController : ControllerBase
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// is .
+ ///
+ protected HttpApiController(ICommandMediator mediator)
+ : base()
+ {
+ Mediator = mediator.RejectIf().IsNull(nameof(mediator)).TargetArgument;
+ }
+
+ ///
+ /// Creates a .
+ ///
+ ///
+ /// The exception that was raised which represents the server error. The default value is .
+ ///
+ ///
+ /// A .
+ ///
+ protected BadRequestObjectResult BadRequest(Exception exception) => BadRequest(exception, null);
+
+ ///
+ /// Creates a .
+ ///
+ ///
+ /// A message to include with the HTTP response. The default value is .
+ ///
+ ///
+ /// A .
+ ///
+ protected BadRequestObjectResult BadRequest(String message) => BadRequest(null, message);
+
+ ///
+ /// Creates a .
+ ///
+ ///
+ /// The exception that was raised which represents the server error. The default value is .
+ ///
+ ///
+ /// A message to include with the HTTP response. The default value is .
+ ///
+ ///
+ /// A .
+ ///
+ protected BadRequestObjectResult BadRequest(Exception exception, String message)
+ {
+ var processedMessage = (message.IsNullOrEmpty() ? exception?.Message : message) ?? DefaultBadRequestMessage;
+ return BadRequest(error: ConveysStackTraceInFailureResponses ? $"{processedMessage} {exception?.StackTrace}" : processedMessage);
+ }
+
+ ///
+ /// Creates an with HTTP status code 500.
+ ///
+ ///
+ /// An with HTTP status code 500.
+ ///
+ protected ObjectResult InternalServerError() => InternalServerError(null, null);
+
+ ///
+ /// Creates an with HTTP status code 500.
+ ///
+ ///
+ /// The exception that was raised which represents the server error. The default value is .
+ ///
+ ///
+ /// An with HTTP status code 500.
+ ///
+ protected ObjectResult InternalServerError(Exception exception) => InternalServerError(exception, null);
+
+ ///
+ /// Creates an with HTTP status code 500.
+ ///
+ ///
+ /// A message to include with the HTTP response. The default value is .
+ ///
+ ///
+ /// An with HTTP status code 500.
+ ///
+ protected ObjectResult InternalServerError(String message) => InternalServerError(null, message);
+
+ ///
+ /// Creates an with HTTP status code 500.
+ ///
+ ///
+ /// The exception that was raised which represents the server error. The default value is .
+ ///
+ ///
+ /// A message to include with the HTTP response. The default value is .
+ ///
+ ///
+ /// An with HTTP status code 500.
+ ///
+ protected ObjectResult InternalServerError(Exception exception, String message)
+ {
+ var processedMessage = (message.IsNullOrEmpty() ? exception?.Message : message) ?? DefaultInternalServerErrorMessage;
+ return StatusCode((Int32)HttpStatusCode.InternalServerError, ConveysStackTraceInFailureResponses ? $"{processedMessage} {exception?.StackTrace}" : processedMessage);
+ }
+
+ ///
+ /// Represents a value indicating whether or not stack trace information is included with failure responses.
+ ///
+ protected virtual Boolean ConveysStackTraceInFailureResponses => DefaultConveysStackTraceInFailureResponses;
+
+ ///
+ /// Represents the standard route for an HTTP API controller.
+ ///
+ protected const String StandardControllerRoute = "[controller]";
+
+ ///
+ /// Represents processing intermediary that is used to process sub-commands.
+ ///
+ protected readonly ICommandMediator Mediator;
+
+ ///
+ /// Represents the default error message that is provided by .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String DefaultBadRequestMessage = "The request was improperly formed or invalid.";
+
+ ///
+ /// Represents the default value indicating whether or not stack trace information is included with failure responses.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Boolean DefaultConveysStackTraceInFailureResponses = false;
+
+ ///
+ /// Represents the default error message that is provided by .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String DefaultInternalServerErrorMessage = "An error occurred on the server while processing the request.";
}
}
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.Web/RapidField.SolidInstruments.Web.csproj b/src/RapidField.SolidInstruments.Web/RapidField.SolidInstruments.Web.csproj
index 79c30e43..a21abe0b 100644
--- a/src/RapidField.SolidInstruments.Web/RapidField.SolidInstruments.Web.csproj
+++ b/src/RapidField.SolidInstruments.Web/RapidField.SolidInstruments.Web.csproj
@@ -40,6 +40,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
+
diff --git a/test/RapidField.SolidInstruments.Core.UnitTests/Caching/InMemoryCacheClientTests.cs b/test/RapidField.SolidInstruments.Core.UnitTests/Caching/InMemoryCacheClientTests.cs
new file mode 100644
index 00000000..545633cd
--- /dev/null
+++ b/test/RapidField.SolidInstruments.Core.UnitTests/Caching/InMemoryCacheClientTests.cs
@@ -0,0 +1,64 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using RapidField.SolidInstruments.Core.Caching;
+using RapidField.SolidInstruments.Core.Extensions;
+using System;
+using System.Security.Cryptography;
+
+namespace RapidField.SolidInstruments.Core.UnitTests.Caching
+{
+ [TestClass]
+ public class InMemoryCacheClientTests
+ {
+ [TestMethod]
+ public void Process_ShouldProduceDesiredResults_ForAggressiveStrategy() => Process_ShouldProduceDesiredResults(InMemoryCachingStrategy.Aggressive, true);
+
+ [TestMethod]
+ public void Process_ShouldProduceDesiredResults_ForConservativeStrategy() => Process_ShouldProduceDesiredResults(InMemoryCachingStrategy.Conservative, true);
+
+ [TestMethod]
+ public void Process_ShouldProduceDesiredResults_ForModerateStrategy() => Process_ShouldProduceDesiredResults(InMemoryCachingStrategy.Moderate, true);
+
+ [TestMethod]
+ public void Process_ShouldProduceDesiredResults_ForNoCachingStrategy() => Process_ShouldProduceDesiredResults(InMemoryCachingStrategy.NoCaching, false);
+
+ private void Process_ShouldProduceDesiredResults(InMemoryCachingStrategy strategy, Boolean cacheShouldBeOperative)
+ {
+ // Arrange.
+ var keyOne = "foo";
+ var keyTwo = "bar";
+ using var randomnessProvider = RandomNumberGenerator.Create();
+ var produceValueFunction = new Func(() => SimulatedModel.Random(randomnessProvider));
+ using var target = new InMemoryCacheClient(strategy);
+
+ // Act.
+ var targetIsOperative = target.IsOperative;
+ var resultOne = target.Process(keyOne, produceValueFunction);
+ var resultTwo = target.Process(keyTwo, produceValueFunction);
+ var resultThree = target.Process(keyOne, produceValueFunction);
+ var resultFour = target.Process(keyTwo, produceValueFunction);
+ var resultOneHashCode = resultOne.GetImpliedHashCode();
+ var resultTwoHashCode = resultTwo.GetImpliedHashCode();
+ var resultThreeHashCode = resultThree.GetImpliedHashCode();
+ var resultFourHashCode = resultFour.GetImpliedHashCode();
+ var resultsOneAndTwoAreEqual = resultOneHashCode == resultTwoHashCode;
+ var resultsOneAndThreeAreEqual = resultOneHashCode == resultThreeHashCode;
+ var resultsOneAndFourAreEqual = resultOneHashCode == resultFourHashCode;
+ var resultsTwoAndThreeAreEqual = resultTwoHashCode == resultThreeHashCode;
+ var resultsTwoAndFourAreEqual = resultTwoHashCode == resultFourHashCode;
+ var resultsThreeAndFourAreEqual = resultThreeHashCode == resultFourHashCode;
+
+ // Assert.
+ Assert.AreEqual(targetIsOperative, cacheShouldBeOperative);
+ Assert.IsFalse(resultsOneAndTwoAreEqual);
+ Assert.AreEqual(resultsOneAndThreeAreEqual, cacheShouldBeOperative);
+ Assert.IsFalse(resultsOneAndFourAreEqual);
+ Assert.IsFalse(resultsTwoAndThreeAreEqual);
+ Assert.AreEqual(resultsTwoAndFourAreEqual, cacheShouldBeOperative);
+ Assert.IsFalse(resultsThreeAndFourAreEqual);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/RapidField.SolidInstruments.Core.UnitTests/Extensions/ObjectExtensionsTests.cs b/test/RapidField.SolidInstruments.Core.UnitTests/Extensions/ObjectExtensionsTests.cs
index e013953c..9a51f2de 100644
--- a/test/RapidField.SolidInstruments.Core.UnitTests/Extensions/ObjectExtensionsTests.cs
+++ b/test/RapidField.SolidInstruments.Core.UnitTests/Extensions/ObjectExtensionsTests.cs
@@ -4,6 +4,7 @@
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
+using RapidField.SolidInstruments.Core.Extensions;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
@@ -13,6 +14,20 @@ namespace RapidField.SolidInstruments.Core.UnitTests.Extensions
[TestClass]
public class ObjectExtensionsTests
{
+ [TestMethod]
+ public void CalculateSizeInBytes_ShouldProduceAccurateResult_ForNullTarget()
+ {
+ // Arrange.
+ var expectedResult = IntPtr.Size;
+ var target = (Object)null;
+
+ // Act.
+ var result = target.CalculateSizeInBytes();
+
+ // Assert.
+ Assert.AreEqual(expectedResult, result);
+ }
+
[TestMethod]
public void GetImpliedHashCode_ShouldProduceIdenticalHashCodes_ForIdenticalObjects()
{
diff --git a/test/RapidField.SolidInstruments.Messaging.UnitTests/SimulatedObject.cs b/test/RapidField.SolidInstruments.Messaging.UnitTests/SimulatedObject.cs
index 449da5c4..5ae0de72 100644
--- a/test/RapidField.SolidInstruments.Messaging.UnitTests/SimulatedObject.cs
+++ b/test/RapidField.SolidInstruments.Messaging.UnitTests/SimulatedObject.cs
@@ -123,19 +123,19 @@ public Boolean Equals(SimulatedObject other)
{
return false;
}
- else if (DecimalValues is null && (other.DecimalValues is null) == false)
+ else if (DecimalValues is null && other.DecimalValues is not null)
{
return false;
}
- else if ((DecimalValues is null) == false && other.DecimalValues is null)
+ else if (DecimalValues is not null && other.DecimalValues is null)
{
return false;
}
- else if (NestedObjects is null && (other.NestedObjects is null) == false)
+ else if (NestedObjects is null && other.NestedObjects is not null)
{
return false;
}
- else if ((NestedObjects is null) == false && other.NestedObjects is null)
+ else if (NestedObjects is not null && other.NestedObjects is null)
{
return false;
}
diff --git a/test/RapidField.SolidInstruments.Serialization.UnitTests/SimulatedObject.cs b/test/RapidField.SolidInstruments.Serialization.UnitTests/SimulatedObject.cs
index df325ab6..66a05fd0 100644
--- a/test/RapidField.SolidInstruments.Serialization.UnitTests/SimulatedObject.cs
+++ b/test/RapidField.SolidInstruments.Serialization.UnitTests/SimulatedObject.cs
@@ -123,19 +123,19 @@ public Boolean Equals(SimulatedObject other)
{
return false;
}
- else if (DecimalValues is null && (other.DecimalValues is null) == false)
+ else if (DecimalValues is null && other.DecimalValues is not null)
{
return false;
}
- else if ((DecimalValues is null) == false && other.DecimalValues is null)
+ else if (DecimalValues is not null && other.DecimalValues is null)
{
return false;
}
- else if (NestedObjects is null && (other.NestedObjects is null) == false)
+ else if (NestedObjects is null && other.NestedObjects is not null)
{
return false;
}
- else if ((NestedObjects is null) == false && other.NestedObjects is null)
+ else if (NestedObjects is not null && other.NestedObjects is null)
{
return false;
}