diff --git a/RapidField.SolidInstruments.sln b/RapidField.SolidInstruments.sln
index a61a4743..6b06ab85 100644
--- a/RapidField.SolidInstruments.sln
+++ b/RapidField.SolidInstruments.sln
@@ -403,7 +403,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RapidField.SolidInstruments
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RapidField.SolidInstruments.Example.Domain.AccessControl.Service", "example\RapidField.SolidInstruments.Example.Domain.AccessControl.Service\RapidField.SolidInstruments.Example.Domain.AccessControl.Service.csproj", "{77026FD0-F0DC-48B8-A678-0E67BDCC29EB}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RapidField.SolidInstruments.Example.BeaconService", "example\RapidField.SolidInstruments.Example.BeaconService\RapidField.SolidInstruments.Example.BeaconService.csproj", "{423CCBD1-0299-4C1A-BFDB-05AF2BF4847A}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RapidField.SolidInstruments.Example.BeaconService", "example\RapidField.SolidInstruments.Example.BeaconService\RapidField.SolidInstruments.Example.BeaconService.csproj", "{423CCBD1-0299-4C1A-BFDB-05AF2BF4847A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi", "example\RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi\RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi.csproj", "{AC2D0816-3ABF-4458-80DB-E4BADDD9A2E0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RapidField.SolidInstruments.Example.Domain.Identity", "example\RapidField.SolidInstruments.Example.Domain.Identity\RapidField.SolidInstruments.Example.Domain.Identity.csproj", "{630F6AC9-3A4C-4B8A-B55D-D44A7BFBB025}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RapidField.SolidInstruments.Example.Domain.Identity.Service", "example\RapidField.SolidInstruments.Example.Domain.Identity.Service\RapidField.SolidInstruments.Example.Domain.Identity.Service.csproj", "{352E9021-1010-4A5D-8435-F8331DA63F95}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -631,6 +637,18 @@ Global
{423CCBD1-0299-4C1A-BFDB-05AF2BF4847A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{423CCBD1-0299-4C1A-BFDB-05AF2BF4847A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{423CCBD1-0299-4C1A-BFDB-05AF2BF4847A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AC2D0816-3ABF-4458-80DB-E4BADDD9A2E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AC2D0816-3ABF-4458-80DB-E4BADDD9A2E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AC2D0816-3ABF-4458-80DB-E4BADDD9A2E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AC2D0816-3ABF-4458-80DB-E4BADDD9A2E0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {630F6AC9-3A4C-4B8A-B55D-D44A7BFBB025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {630F6AC9-3A4C-4B8A-B55D-D44A7BFBB025}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {630F6AC9-3A4C-4B8A-B55D-D44A7BFBB025}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {630F6AC9-3A4C-4B8A-B55D-D44A7BFBB025}.Release|Any CPU.Build.0 = Release|Any CPU
+ {352E9021-1010-4A5D-8435-F8331DA63F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {352E9021-1010-4A5D-8435-F8331DA63F95}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {352E9021-1010-4A5D-8435-F8331DA63F95}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {352E9021-1010-4A5D-8435-F8331DA63F95}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -707,6 +725,9 @@ Global
{7B185E3A-3A26-4188-9FB4-DED7E4EF5C3D} = {BEB60D41-11BB-4C6E-8F5D-1E7990A27C34}
{77026FD0-F0DC-48B8-A678-0E67BDCC29EB} = {BEB60D41-11BB-4C6E-8F5D-1E7990A27C34}
{423CCBD1-0299-4C1A-BFDB-05AF2BF4847A} = {BEB60D41-11BB-4C6E-8F5D-1E7990A27C34}
+ {AC2D0816-3ABF-4458-80DB-E4BADDD9A2E0} = {BEB60D41-11BB-4C6E-8F5D-1E7990A27C34}
+ {630F6AC9-3A4C-4B8A-B55D-D44A7BFBB025} = {BEB60D41-11BB-4C6E-8F5D-1E7990A27C34}
+ {352E9021-1010-4A5D-8435-F8331DA63F95} = {BEB60D41-11BB-4C6E-8F5D-1E7990A27C34}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {834FCFB0-DA00-4ABD-9424-6FE1398A9F6E}
diff --git a/cicd/modules/BuildAndDeployment.psm1 b/cicd/modules/BuildAndDeployment.psm1
index 4e2f3c2a..f0b92d0b 100644
--- a/cicd/modules/BuildAndDeployment.psm1
+++ b/cicd/modules/BuildAndDeployment.psm1
@@ -87,8 +87,10 @@ $SolutionConfigurationDebug = "Debug";
$SolutionConfigurationRelease = "Release";
# Namespaces
+$ExampleAccessControlHttpApiApplicationNamespace = "RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi";
$ExampleAccessControlServiceApplicationNamespace = "RapidField.SolidInstruments.Example.Domain.AccessControl.Service";
$ExampleBeaconServiceApplicationNamespace = "RapidField.SolidInstruments.Example.BeaconService";
+$ExampleIdentityServiceApplicationNamespace = "RapidField.SolidInstruments.Example.Domain.Identity.Service";
# Regular expressions
$ValidCommitMessageRegularExpressionPattern = "^(#[1-9][0-9]{0,4} )?[A-Z][A-Za-z0-9\,\.\!\;\:\'\""\@\#\$\%\^\&\*\-\+\=_\(\)\[\]\{\}\|\\\/\s]{8,144}$";
@@ -113,7 +115,8 @@ $RepositoryName = $env:APPVEYOR_REPO_NAME;
$TagName = $env:APPVEYOR_REPO_TAG_NAME;
# Other configuration values
-$TargetFrameworkForExampleServiceApplication = "netcoreapp2.1";
+$TargetFrameworkForExampleHttpApiApplications = "netcoreapp3.1";
+$TargetFrameworkForExampleServiceApplications = "netcoreapp3.1";
# Modules
Import-Module $FilePathForCoreModule -Force;
@@ -609,7 +612,48 @@ Function SignPackages
<#
.Synopsis
-Starts the example access control service application.
+Starts the example AccessControl domain HTTP API application.
+#>
+Function StartExampleAccessControlHttpApiApplication
+{
+ Param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [String] $SolutionConfiguration
+ )
+
+ ComposeStart "Starting the example AccessControl domain HTTP API application using $SolutionConfiguration configuration.";
+ $BinaryDirectoryPath = Join-Path -Path "$DirectoryPathForExample" -ChildPath "$ExampleAccessControlHttpApiApplicationNamespace\bin\$SolutionConfiguration\$TargetFrameworkForExampleHttpApiApplications";
+ $BinaryFileName = "$ExampleAccessControlHttpApiApplicationNamespace.dll";
+ $BinaryFilePath = Join-Path -Path "$BinaryDirectoryPath" -ChildPath "$BinaryFileName";
+ ComposeNormal "Using binary path: $BinaryFilePath";
+ Push-Location "$BinaryDirectoryPath";
+ Start-Process -ArgumentList "$BinaryFileName" -FilePath "dotnet" -WindowStyle Minimized;
+ Pop-Location;
+ ComposeFinish "Finished starting the application.";
+}
+
+<#
+.Synopsis
+Starts the example AccessControl domain HTTP API application in debug mode.
+#>
+Function StartExampleAccessControlHttpApiApplicationDebug
+{
+ StartExampleAccessControlHttpApiApplication -SolutionConfiguration $SolutionConfigurationDebug;
+}
+
+<#
+.Synopsis
+Starts the example AccessControl domain HTTP API application in release mode.
+#>
+Function StartExampleAccessControlHttpApiApplicationRelease
+{
+ StartExampleAccessControlHttpApiApplication -SolutionConfiguration $SolutionConfigurationRelease;
+}
+
+<#
+.Synopsis
+Starts the example AccessControl domain service application.
#>
Function StartExampleAccessControlServiceApplication
{
@@ -619,16 +663,20 @@ Function StartExampleAccessControlServiceApplication
[String] $SolutionConfiguration
)
- ComposeStart "Starting the example access control service application using $SolutionConfiguration configuration.";
- $BinaryFilePath = Join-Path -Path "$DirectoryPathForExample" -ChildPath "$ExampleServiceApplicationNamespace\bin\$SolutionConfiguration\$TargetFrameworkForExampleServiceApplication\$ExampleAccessControlServiceApplicationNamespace.dll";
+ ComposeStart "Starting the example AccessControl domain service application using $SolutionConfiguration configuration.";
+ $BinaryDirectoryPath = Join-Path -Path "$DirectoryPathForExample" -ChildPath "$ExampleAccessControlServiceApplicationNamespace\bin\$SolutionConfiguration\$TargetFrameworkForExampleServiceApplications";
+ $BinaryFileName = "$ExampleAccessControlServiceApplicationNamespace.dll";
+ $BinaryFilePath = Join-Path -Path "$BinaryDirectoryPath" -ChildPath "$BinaryFileName";
ComposeNormal "Using binary path: $BinaryFilePath";
- Start-Process -ArgumentList "$BinaryFilePath" -FilePath "dotnet" -WindowStyle Minimized;
+ Push-Location "$BinaryDirectoryPath";
+ Start-Process -ArgumentList "$BinaryFileName" -FilePath "dotnet" -WindowStyle Minimized;
+ Pop-Location;
ComposeFinish "Finished starting the application.";
}
<#
.Synopsis
-Starts the example access control service application in debug mode.
+Starts the example AccessControl domain service application in debug mode.
#>
Function StartExampleAccessControlServiceApplicationDebug
{
@@ -637,7 +685,7 @@ Function StartExampleAccessControlServiceApplicationDebug
<#
.Synopsis
-Starts the example access control service application in release mode.
+Starts the example AccessControl domain service application in release mode.
#>
Function StartExampleAccessControlServiceApplicationRelease
{
@@ -646,7 +694,7 @@ Function StartExampleAccessControlServiceApplicationRelease
<#
.Synopsis
-Starts the example beacon service application.
+Starts the example Beacon service application.
#>
Function StartExampleBeaconServiceApplication
{
@@ -657,15 +705,19 @@ Function StartExampleBeaconServiceApplication
)
ComposeStart "Starting the example beacon service application using $SolutionConfiguration configuration.";
- $BinaryFilePath = Join-Path -Path "$DirectoryPathForExample" -ChildPath "$ExampleServiceApplicationNamespace\bin\$SolutionConfiguration\$TargetFrameworkForExampleServiceApplication\$ExampleBeaconServiceApplicationNamespace.dll";
+ $BinaryDirectoryPath = Join-Path -Path "$DirectoryPathForExample" -ChildPath "$ExampleBeaconServiceApplicationNamespace\bin\$SolutionConfiguration\$TargetFrameworkForExampleServiceApplications";
+ $BinaryFileName = "$ExampleBeaconServiceApplicationNamespace.dll";
+ $BinaryFilePath = Join-Path -Path "$BinaryDirectoryPath" -ChildPath "$BinaryFileName";
ComposeNormal "Using binary path: $BinaryFilePath";
- Start-Process -ArgumentList "$BinaryFilePath" -FilePath "dotnet" -WindowStyle Minimized;
+ Push-Location "$BinaryDirectoryPath";
+ Start-Process -ArgumentList "$BinaryFileName" -FilePath "dotnet" -WindowStyle Minimized;
+ Pop-Location;
ComposeFinish "Finished starting the application.";
}
<#
.Synopsis
-Starts the example beacon service application in debug mode.
+Starts the example Beacon service application in debug mode.
#>
Function StartExampleBeaconServiceApplicationDebug
{
@@ -674,13 +726,54 @@ Function StartExampleBeaconServiceApplicationDebug
<#
.Synopsis
-Starts the example beacon service application in release mode.
+Starts the example Beacon service application in release mode.
#>
Function StartExampleBeaconServiceApplicationRelease
{
StartExampleBeaconServiceApplication -SolutionConfiguration $SolutionConfigurationRelease;
}
+<#
+.Synopsis
+Starts the example Identity domain service application.
+#>
+Function StartExampleIdentityServiceApplication
+{
+ Param
+ (
+ [Parameter(Mandatory = $true, Position = 0)]
+ [String] $SolutionConfiguration
+ )
+
+ ComposeStart "Starting the example Identity domain service application using $SolutionConfiguration configuration.";
+ $BinaryDirectoryPath = Join-Path -Path "$DirectoryPathForExample" -ChildPath "$ExampleIdentityServiceApplicationNamespace\bin\$SolutionConfiguration\$TargetFrameworkForExampleServiceApplications";
+ $BinaryFileName = "$ExampleIdentityServiceApplicationNamespace.dll";
+ $BinaryFilePath = Join-Path -Path "$BinaryDirectoryPath" -ChildPath "$BinaryFileName";
+ ComposeNormal "Using binary path: $BinaryFilePath";
+ Push-Location "$BinaryDirectoryPath";
+ Start-Process -ArgumentList "$BinaryFileName" -FilePath "dotnet" -WindowStyle Minimized;
+ Pop-Location;
+ ComposeFinish "Finished starting the application.";
+}
+
+<#
+.Synopsis
+Starts the example Identity domain service application in debug mode.
+#>
+Function StartExampleIdentityServiceApplicationDebug
+{
+ StartExampleIdentityServiceApplication -SolutionConfiguration $SolutionConfigurationDebug;
+}
+
+<#
+.Synopsis
+Starts the example Identity domain service application in release mode.
+#>
+Function StartExampleIdentityServiceApplicationRelease
+{
+ StartExampleIdentityServiceApplication -SolutionConfiguration $SolutionConfigurationRelease;
+}
+
<#
.Synopsis
Stops all running .NET applications.
diff --git a/example/README.md b/example/README.md
index 5c210b9b..e0c130a1 100644
--- a/example/README.md
+++ b/example/README.md
@@ -15,8 +15,11 @@ This path contains sample projects that utilize the **Solid Instruments** [const
- [`RapidField.SolidInstruments.Example.BeaconService`](/example/RapidField.SolidInstruments.Example.BeaconService) demonstrates a utility service that publishes scheduled heartbeat messages.
- [`RapidField.SolidInstruments.Example.Domain`](/example/RapidField.SolidInstruments.Example.Domain) houses a sample collection of shared domain models that utilize the **Solid Instruments** [messaging](../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
-- [`RapidField.SolidInstruments.Example.Domain.AccessControl`](/example/RapidField.SolidInstruments.Example.Domain.AccessControl) demonstrates access control domain logic utilizing the **Solid Instruments** [data access](../src/RapidField.SolidInstruments.DataAccess/README.md) constructs.
-- [`RapidField.SolidInstruments.Example.Domain.AccessControl.Service`](/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service) demonstrates an access control domain service utilizing the **Solid Instruments** [data access](../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
+- [`RapidField.SolidInstruments.Example.Domain.AccessControl`](/example/RapidField.SolidInstruments.Example.Domain.AccessControl) demonstrates AccessControl domain logic utilizing the **Solid Instruments** [data access](../src/RapidField.SolidInstruments.DataAccess/README.md) constructs.
+- [`RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi`](/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi) demonstrates an AccessControl domain HTTP API utilizing the **Solid Instruments** [data access](../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
+- [`RapidField.SolidInstruments.Example.Domain.AccessControl.Service`](/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service) demonstrates an AccessControl domain service utilizing the **Solid Instruments** [data access](../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
+- [`RapidField.SolidInstruments.Example.Domain.Identity`](/example/RapidField.SolidInstruments.Example.Domain.Identity) demonstrates Identity domain logic utilizing the **Solid Instruments** [data access](../src/RapidField.SolidInstruments.DataAccess/README.md) constructs.
+- [`RapidField.SolidInstruments.Example.Domain.Identity.Service`](/example/RapidField.SolidInstruments.Example.Domain.Identity.Service) demonstrates an Identity domain service utilizing the **Solid Instruments** [data access](../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
## License
diff --git a/example/RapidField.SolidInstruments.Example.BeaconService/RapidField.SolidInstruments.Example.BeaconService.csproj b/example/RapidField.SolidInstruments.Example.BeaconService/RapidField.SolidInstruments.Example.BeaconService.csproj
index b0180320..9f36872d 100644
--- a/example/RapidField.SolidInstruments.Example.BeaconService/RapidField.SolidInstruments.Example.BeaconService.csproj
+++ b/example/RapidField.SolidInstruments.Example.BeaconService/RapidField.SolidInstruments.Example.BeaconService.csproj
@@ -34,7 +34,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/ApplicationDependencyPackage.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/ApplicationDependencyPackage.cs
new file mode 100644
index 00000000..fc762217
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/ApplicationDependencyPackage.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 Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using RapidField.SolidInstruments.InversionOfControl;
+using RapidField.SolidInstruments.InversionOfControl.DotNetNative;
+using System.Collections.Generic;
+
+namespace RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi
+{
+ ///
+ /// Encapsulates container configuration for the application.
+ ///
+ public class ApplicationDependencyPackage : DotNetNativeDependencyPackage
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ApplicationDependencyPackage()
+ : base()
+ {
+ return;
+ }
+
+ ///
+ /// Creates a new collection of dependency modules for the package.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// The package's dependency modules.
+ ///
+ protected override IEnumerable> CreateModules(IConfiguration applicationConfiguration) => new IDependencyModule[]
+ {
+ new DatabaseContextDependencyModule(applicationConfiguration),
+ new ServiceBusDependencyModule(applicationConfiguration),
+ new MessageHandlerModule(applicationConfiguration)
+ };
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Controllers/UserController.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Controllers/UserController.cs
new file mode 100644
index 00000000..eed70a41
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Controllers/UserController.cs
@@ -0,0 +1,152 @@
+// =================================================================================================================================
+// 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.Example.Domain.Commands.ModelState.User;
+using RapidField.SolidInstruments.Example.Domain.Messages.Command.ModelState.User;
+using RapidField.SolidInstruments.Example.Domain.Models.User;
+using System;
+using System.Diagnostics;
+using System.Net;
+
+namespace RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi.Controllers
+{
+ ///
+ /// Processes HTTP requests for the ~/User endpoint.
+ ///
+ [ApiController]
+ [Route("[controller]")]
+ public sealed class UserController : ControllerBase
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// is .
+ ///
+ public UserController(ICommandMediator mediator)
+ {
+ Mediator = mediator.RejectIf().IsNull(nameof(mediator)).TargetArgument;
+ }
+
+ ///
+ /// Handles DELETE requests for the endpoint.
+ ///
+ ///
+ /// An identifier by which to find the .
+ ///
+ ///
+ /// A status code result.
+ ///
+ [HttpDelete]
+ public IActionResult Delete([FromQuery] Guid identifier)
+ {
+ try
+ {
+ return Ok();
+ }
+ catch (ArgumentException exception)
+ {
+ return BadRequest($"{exception.Message} {exception.StackTrace}");
+ }
+ catch (Exception exception)
+ {
+ return StatusCode((Int32)HttpStatusCode.InternalServerError, $"{exception.Message} {exception.StackTrace}");
+ }
+ }
+
+ ///
+ /// Handles GET requests for the endpoint.
+ ///
+ ///
+ /// An identifier by which to find the .
+ ///
+ ///
+ /// A with matching , or if no matching
+ /// model exists.
+ ///
+ [HttpGet]
+ public IActionResult Get([FromQuery] Guid identifier)
+ {
+ try
+ {
+ var model = (DomainModel)null;
+ return new JsonResult(model);
+ }
+ catch (ArgumentException exception)
+ {
+ return BadRequest($"{exception.Message} {exception.StackTrace}");
+ }
+ catch (Exception exception)
+ {
+ return StatusCode((Int32)HttpStatusCode.InternalServerError, $"{exception.Message} {exception.StackTrace}");
+ }
+ }
+
+ ///
+ /// Handles POST requests for the endpoint.
+ ///
+ ///
+ /// The to create.
+ ///
+ ///
+ /// A status code result.
+ ///
+ [HttpPost]
+ public IActionResult Post([FromBody] DomainModel model)
+ {
+ try
+ {
+ _ = Mediator.Process(new CreateDomainModelCommandMessage(new CreateDomainModelCommand(model)));
+ return Ok();
+ }
+ catch (ArgumentException exception)
+ {
+ return BadRequest($"{exception.Message} {exception.StackTrace}");
+ }
+ catch (Exception exception)
+ {
+ return StatusCode((Int32)HttpStatusCode.InternalServerError, $"{exception.Message} {exception.StackTrace}");
+ }
+ }
+
+ ///
+ /// Handles PUT requests for the endpoint.
+ ///
+ ///
+ /// The to create.
+ ///
+ ///
+ /// A status code result.
+ ///
+ [HttpPut]
+ public IActionResult Put([FromBody] DomainModel model)
+ {
+ try
+ {
+ _ = Mediator.Process(new UpdateDomainModelCommandMessage(new UpdateDomainModelCommand(model)));
+ return Ok();
+ }
+ catch (ArgumentException exception)
+ {
+ return BadRequest($"{exception.Message} {exception.StackTrace}");
+ }
+ catch (Exception exception)
+ {
+ return StatusCode((Int32)HttpStatusCode.InternalServerError, $"{exception.Message} {exception.StackTrace}");
+ }
+ }
+
+ ///
+ /// Represents processing intermediary that is used to process sub-commands.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly ICommandMediator Mediator;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Program.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Program.cs
new file mode 100644
index 00000000..8ac60e1a
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Program.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 Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+using System;
+
+namespace RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi
+{
+ ///
+ /// Houses the entry point for the application.
+ ///
+ public static class Program
+ {
+ ///
+ /// Configures the application hosting environment.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime.
+ ///
+ ///
+ /// The resulting host configuration.
+ ///
+ public static IHostBuilder CreateHostBuilder(String[] args) => Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.UseStartup();
+ });
+
+ ///
+ /// Begins execution of the application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime.
+ ///
+ public static void Main(String[] args) => CreateHostBuilder(args).Build().Run();
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Properties/launchSettings.json b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Properties/launchSettings.json
new file mode 100644
index 00000000..9a6041f5
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Properties/launchSettings.json
@@ -0,0 +1,28 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:57484",
+ "sslPort": 44319
+ }
+ },
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "launchUrl": "weatherforecast",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/README.md b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/README.md
new file mode 100644
index 00000000..72ef86e3
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/README.md
@@ -0,0 +1,30 @@
+
+
+[![Solid Instruments](../../SolidInstruments.Logo.Color.Transparent.500w.png)](../../README.md)
+- - -
+
+# RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi
+
+This document describes the purpose of the [`RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi`]() project.
+
+## Purpose
+
+This project demonstrates an AccessControl domain HTTP API utilizing the **Solid Instruments** [data access](../../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
+
+## License
+
+[![License](https://img.shields.io/github/license/rapidfield/solid-instruments?style=flat&color=lightseagreen&label=license&logo=open-access&logoColor=lightgrey)](../../LICENSE.txt)
+
+**Solid Instruments** is [MIT-licensed](https://en.wikipedia.org/wiki/MIT_License). Review the [license terms](../../LICENSE.txt) for more information.
+
+
+
+- - -
+
+
+
+[![RapidField](../../RapidField.Logo.Color.Black.Transparent.200w.png)](https://www.rapidfield.com)
+
+###### Copyright (c) RapidField LLC. All rights reserved. "RapidField" and "Solid Instruments" are trademarks of RapidField LLC.
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi.csproj b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi.csproj
new file mode 100644
index 00000000..3ce434a5
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi.csproj
@@ -0,0 +1,25 @@
+
+
+
+
+ Solid Instruments contributors
+ RapidField
+ Copyright (c) RapidField LLC. All rights reserved.
+ Solid Instruments
+ This project demonstrates an AccessControl domain HTTP API application utilizing Solid Instruments constructs.
+ $(BuildVersion)
+ netcoreapp3.1
+ latest
+
+
+ true
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Startup.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Startup.cs
new file mode 100644
index 00000000..34859fc7
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/Startup.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.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+#pragma warning disable CA1822 // Mark members as static
+
+namespace RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi
+{
+ ///
+ /// Encapsulates application startup configuration.
+ ///
+ public class Startup
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ ///
+ ///
+ ///
+ /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ app.UseHttpsRedirection();
+ app.UseRouting();
+ app.UseAuthorization();
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+
+ ///
+ ///
+ ///
+ /// This method gets called by the runtime. Use this method to add services to the container.
+ ///
+ ///
+ ///
+ public void ConfigureServices(IServiceCollection services) => _ = services.AddControllers();
+
+ ///
+ ///
+ public IConfiguration Configuration { get; }
+ }
+}
+
+#pragma warning restore CA1822 // Mark members as static
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/appsettings.Development.json b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/appsettings.Development.json
new file mode 100644
index 00000000..2905c113
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/appsettings.Development.json
@@ -0,0 +1,13 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/appsettings.json b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/appsettings.json
new file mode 100644
index 00000000..10084b9a
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.HttpApi/appsettings.json
@@ -0,0 +1,18 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+{
+ "AllowedHosts": "*",
+ "ConnectionStrings": {
+ "AccessControl": "Server=(LocalDB)\\MSSQLLocalDB;Database=AccessControl;Trusted_Connection=True;",
+ "ExampleServiceBus": "amqp://guest:guest@localhost:5672"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs
index d76d7263..4e4fd0e9 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/ApplicationServiceExecutor.cs
@@ -55,32 +55,9 @@ protected override void AddListeners(IMessageListeningProfile listeningProfile,
{
try
{
- // Add standard listeners.
- listeningProfile.AddExceptionRaisedEventListener();
- listeningProfile.AddHeartbeatListener();
- listeningProfile.AddPingRequestListener();
-
- // Add command listeners.
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
- listeningProfile.AddCommandListener();
-
- // Add event listeners.
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
- listeningProfile.AddEventListener();
+ AddStandardListeners(listeningProfile, applicationConfiguration, commandLineArguments);
+ AddCommandListeners(listeningProfile, applicationConfiguration, commandLineArguments);
+ AddEventListeners(listeningProfile, applicationConfiguration, commandLineArguments);
}
finally
{
@@ -130,10 +107,9 @@ protected override void OnExecutionStarting(IDependencyScope dependencyScope, IC
{
try
{
- var mediator = dependencyScope.Resolve();
- TestServiceBusConnectivity(mediator);
+ TestServiceBusConnectivity(dependencyScope);
MigrateDatabaseSchema(dependencyScope);
- HydrateNamedEntities(mediator);
+ HydrateNamedDomainEntities(dependencyScope);
}
finally
{
@@ -141,36 +117,138 @@ protected override void OnExecutionStarting(IDependencyScope dependencyScope, IC
}
}
+ ///
+ /// Adds command message listeners to the service.
+ ///
+ ///
+ /// An object that is used to add listeners.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime, if any.
+ ///
+ [DebuggerHidden]
+ private static void AddCommandListeners(IMessageListeningProfile listeningProfile, IConfiguration applicationConfiguration, String[] commandLineArguments)
+ {
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ listeningProfile.AddCommandListener();
+ }
+
+ ///
+ /// Adds event message listeners to the service.
+ ///
+ ///
+ /// An object that is used to add listeners.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime, if any.
+ ///
+ [DebuggerHidden]
+ private static void AddEventListeners(IMessageListeningProfile listeningProfile, IConfiguration applicationConfiguration, String[] commandLineArguments)
+ {
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ }
+
+ ///
+ /// Adds standard message listeners to the service.
+ ///
+ ///
+ /// An object that is used to add listeners.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime, if any.
+ ///
+ [DebuggerHidden]
+ private static void AddStandardListeners(IMessageListeningProfile listeningProfile, IConfiguration applicationConfiguration, String[] commandLineArguments) => listeningProfile.AddHeartbeatListener();
+
///
/// Creates or updates all named domain models.
///
+ ///
+ /// A scope that is used to resolve service dependencies.
+ ///
+ [DebuggerHidden]
+ private static void HydrateNamedDomainEntities(IDependencyScope dependencyScope)
+ {
+ var mediator = dependencyScope.Resolve();
+ HydrateNamedUsers(mediator);
+ HydrateNamedUserRoles(mediator);
+ HydrateNamedUserRoleAssignments(mediator);
+ Console.WriteLine("Database entities created/updated.");
+ }
+
+ ///
+ /// Creates or updates all named instances.
+ ///
///
/// A mediator that is used to issue commands.
///
[DebuggerHidden]
- private static void HydrateNamedEntities(ICommandMediator mediator)
+ private static void HydrateNamedUserRoleAssignments(ICommandMediator mediator)
{
- foreach (var domainModel in Models.User.DomainModel.Named.All())
+ foreach (var domainModel in Models.UserRoleAssignment.DomainModel.Named.All())
{
- mediator.Process(new Messages.Command.ModelState.User.CreateDomainModelCommandMessage(new Commands.ModelState.User.CreateDomainModelCommand(domainModel)));
+ mediator.Process(new Messages.Command.ModelState.UserRoleAssignment.CreateDomainModelCommandMessage(new Commands.ModelState.UserRoleAssignment.CreateDomainModelCommand(domainModel)));
}
- Thread.Sleep(610);
+ Pause();
+ }
+ ///
+ /// Creates or updates all named instances.
+ ///
+ ///
+ /// A mediator that is used to issue commands.
+ ///
+ [DebuggerHidden]
+ private static void HydrateNamedUserRoles(ICommandMediator mediator)
+ {
foreach (var domainModel in Models.UserRole.DomainModel.Named.All())
{
mediator.Process(new Messages.Command.ModelState.UserRole.CreateDomainModelCommandMessage(new Commands.ModelState.UserRole.CreateDomainModelCommand(domainModel)));
}
- Thread.Sleep(610);
+ Pause();
+ }
- foreach (var domainModel in Models.UserRoleAssignment.DomainModel.Named.All())
+ ///
+ /// Creates or updates all named instances.
+ ///
+ ///
+ /// A mediator that is used to issue commands.
+ ///
+ [DebuggerHidden]
+ private static void HydrateNamedUsers(ICommandMediator mediator)
+ {
+ foreach (var domainModel in Models.User.DomainModel.Named.All())
{
- mediator.Process(new Messages.Command.ModelState.UserRoleAssignment.CreateDomainModelCommandMessage(new Commands.ModelState.UserRoleAssignment.CreateDomainModelCommand(domainModel)));
+ mediator.Process(new Messages.Command.ModelState.User.CreateDomainModelCommandMessage(new Commands.ModelState.User.CreateDomainModelCommand(domainModel)));
}
- Thread.Sleep(610);
- Console.WriteLine("Database entities created/updated.");
+ Pause();
}
///
@@ -184,21 +262,31 @@ private static void MigrateDatabaseSchema(IDependencyScope dependencyScope)
{
var databaseContext = dependencyScope.Resolve();
databaseContext.Database.EnsureCreated();
- Thread.Sleep(610);
+ Pause();
Console.WriteLine("Database schema created/updated.");
}
+ ///
+ /// Blocks the current thread for a short period to ensure synchronous execution of startup events.
+ ///
+ [DebuggerHidden]
+ private static void Pause() => Thread.Sleep(PauseDurationInMilliseconds);
+
///
/// Raises an exception if a service bus connection is unavailable.
///
- ///
- /// A mediator that is used to issue commands.
+ ///
+ /// A scope that is used to resolve service dependencies.
///
+ ///
+ /// The service was unable to verify service bus connectivity.
+ ///
[DebuggerHidden]
- private static void TestServiceBusConnectivity(ICommandMediator mediator)
+ private static void TestServiceBusConnectivity(IDependencyScope dependencyScope)
{
try
{
+ var mediator = dependencyScope.Resolve();
var pingRequestCorrelationIdentifier = Guid.NewGuid();
var pingResponseMessage = mediator.Process(new PingRequestMessage(pingRequestCorrelationIdentifier));
@@ -226,5 +314,11 @@ private static void TestServiceBusConnectivity(ICommandMediator mediator)
/// the start of service execution.
///
protected override sealed String ProductName => "Solid Instruments";
+
+ ///
+ /// Represents the number of milliseconds to wait when invoking .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 PauseDurationInMilliseconds = 1597;
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/Program.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/Program.cs
index baf03cb0..70cf9a02 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/Program.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/Program.cs
@@ -28,6 +28,6 @@ public static void Main(String[] args)
/// Represents the name of the service that is hosted by this application.
///
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
- private const String ServiceName = "AccessControl Service";
+ private static readonly String ServiceName = $"{nameof(AccessControl)} Service";
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/README.md b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/README.md
index 0cab6f2a..a16216b9 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/README.md
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/README.md
@@ -11,7 +11,7 @@ This document describes the purpose of the [`RapidField.SolidInstruments.Example
## Purpose
-This project demonstrates an access control domain service utilizing the **Solid Instruments** [data access](../../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
+This project demonstrates an AccessControl domain service utilizing the **Solid Instruments** [data access](../../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
## License
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/RapidField.SolidInstruments.Example.Domain.AccessControl.Service.csproj b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/RapidField.SolidInstruments.Example.Domain.AccessControl.Service.csproj
index a0a333fd..6ec75bf1 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/RapidField.SolidInstruments.Example.Domain.AccessControl.Service.csproj
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl.Service/RapidField.SolidInstruments.Example.Domain.AccessControl.Service.csproj
@@ -8,7 +8,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
RapidField
Copyright (c) RapidField LLC. All rights reserved.
Solid Instruments
- This project demonstrates an access control service application utilizing Solid Instruments constructs.
+ This project demonstrates an AccessControl domain service application utilizing Solid Instruments constructs.
$(BuildVersion)
Exe
netcoreapp3.1
@@ -34,7 +34,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/CommandHandlerModule.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/CommandHandlerModule.cs
index 457a82c4..66033f63 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/CommandHandlerModule.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/CommandHandlerModule.cs
@@ -9,7 +9,7 @@
namespace RapidField.SolidInstruments.Example.Domain.AccessControl
{
///
- /// Encapsulates container configuration for access control domain command handlers.
+ /// Encapsulates container configuration for AccessControl domain command handlers.
///
public sealed class CommandHandlerModule : DotNetNativeCommandHandlerModule
{
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlerModule.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlerModule.cs
index ea51f7c6..8d04bbed 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlerModule.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlerModule.cs
@@ -9,7 +9,7 @@
namespace RapidField.SolidInstruments.Example.Domain.AccessControl
{
///
- /// Encapsulates container configuration for access control domain event handlers.
+ /// Encapsulates container configuration for AccessControl domain event handlers.
///
public sealed class EventHandlerModule : DotNetNativeEventHandlerModule
{
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelCreatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelCreatedEventHandler.cs
index 27f11f53..3084e6e6 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelCreatedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelCreatedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelCreatedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelDeletedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelDeletedEventHandler.cs
index ba74bbe1..dd50f1bf 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelDeletedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelDeletedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelDeletedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelUpdatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelUpdatedEventHandler.cs
index 34875c73..4ced67f7 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelUpdatedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/User/DomainModelUpdatedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelUpdatedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelCreatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelCreatedEventHandler.cs
index b3f94576..a8c0986f 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelCreatedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelCreatedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelCreatedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelDeletedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelDeletedEventHandler.cs
index c657a3eb..0de0ea1b 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelDeletedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelDeletedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelDeletedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelUpdatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelUpdatedEventHandler.cs
index 85d061a0..887436d3 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelUpdatedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRole/DomainModelUpdatedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelUpdatedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelCreatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelCreatedEventHandler.cs
index be30b0e9..16d0c2b9 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelCreatedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelCreatedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelCreatedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelDeletedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelDeletedEventHandler.cs
index c26e6aad..d0b76947 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelDeletedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelDeletedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelDeletedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelUpdatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelUpdatedEventHandler.cs
index 4616d5f4..3ee32cec 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelUpdatedEventHandler.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/EventHandlers/ModelState/UserRoleAssignment/DomainModelUpdatedEventHandler.cs
@@ -59,6 +59,6 @@ public DomainModelUpdatedEventHandler(ICommandMediator mediator)
///
/// A token that represents and manages contextual thread safety.
///
- protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model?.Identifier.ToSerializedString()}.");
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken) => Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
}
}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/README.md b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/README.md
index 1487d585..b2e90acb 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/README.md
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/README.md
@@ -11,7 +11,7 @@ This document describes the purpose of the [`RapidField.SolidInstruments.Example
## Purpose
-This project demonstrates access control domain logic utilizing the **Solid Instruments** [data access](../../src/RapidField.SolidInstruments.DataAccess/README.md) constructs.
+This project demonstrates AccessControl domain logic utilizing the **Solid Instruments** [data access](../../src/RapidField.SolidInstruments.DataAccess/README.md) constructs.
## License
diff --git a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/RapidField.SolidInstruments.Example.Domain.AccessControl.csproj b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/RapidField.SolidInstruments.Example.Domain.AccessControl.csproj
index 81c381fb..6221e904 100644
--- a/example/RapidField.SolidInstruments.Example.Domain.AccessControl/RapidField.SolidInstruments.Example.Domain.AccessControl.csproj
+++ b/example/RapidField.SolidInstruments.Example.Domain.AccessControl/RapidField.SolidInstruments.Example.Domain.AccessControl.csproj
@@ -8,7 +8,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
RapidField
Copyright (c) RapidField LLC. All rights reserved.
Solid Instruments
- This project demonstrates access control domain logic utilizing the Solid Instruments data access and messaging constructs.
+ This project demonstrates AccessControl domain logic utilizing the Solid Instruments data access and messaging constructs.
$(BuildVersion)
netstandard2.1
latest
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationDependencyPackage.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationDependencyPackage.cs
new file mode 100644
index 00000000..2cdeeecd
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationDependencyPackage.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 Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using RapidField.SolidInstruments.InversionOfControl;
+using RapidField.SolidInstruments.InversionOfControl.DotNetNative;
+using System.Collections.Generic;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.Service
+{
+ ///
+ /// Encapsulates container configuration for the application.
+ ///
+ public class ApplicationDependencyPackage : DotNetNativeDependencyPackage
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ApplicationDependencyPackage()
+ : base()
+ {
+ return;
+ }
+
+ ///
+ /// Creates a new collection of dependency modules for the package.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// The package's dependency modules.
+ ///
+ protected override IEnumerable> CreateModules(IConfiguration applicationConfiguration) => new IDependencyModule[]
+ {
+ new DatabaseContextDependencyModule(applicationConfiguration),
+ new ServiceBusDependencyModule(applicationConfiguration),
+ new EventHandlerModule(applicationConfiguration),
+ new MessageHandlerModule(applicationConfiguration)
+ };
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationServiceExecutor.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationServiceExecutor.cs
new file mode 100644
index 00000000..9b1e152f
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/ApplicationServiceExecutor.cs
@@ -0,0 +1,251 @@
+// =================================================================================================================================
+// 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.Command;
+using RapidField.SolidInstruments.Core;
+using RapidField.SolidInstruments.InversionOfControl;
+using RapidField.SolidInstruments.Messaging.DotNetNative.Service;
+using RapidField.SolidInstruments.Messaging.Service;
+using RapidField.SolidInstruments.Service;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.Service
+{
+ ///
+ /// Prepares for and performs execution of the Identity domain service.
+ ///
+ public sealed class ApplicationServiceExecutor : DotNetNativeMessagingServiceExecutor
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The name of the service.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ public ApplicationServiceExecutor(String serviceName)
+ : base(serviceName)
+ {
+ return;
+ }
+
+ ///
+ /// Adds message listeners to the service.
+ ///
+ ///
+ /// An object that is used to add listeners.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime, if any.
+ ///
+ protected override void AddListeners(IMessageListeningProfile listeningProfile, IConfiguration applicationConfiguration, String[] commandLineArguments)
+ {
+ try
+ {
+ AddStandardListeners(listeningProfile, applicationConfiguration, commandLineArguments);
+ AddCommandListeners(listeningProfile, applicationConfiguration, commandLineArguments);
+ AddEventListeners(listeningProfile, applicationConfiguration, commandLineArguments);
+ }
+ finally
+ {
+ base.AddListeners(listeningProfile, applicationConfiguration, commandLineArguments);
+ }
+ }
+
+ ///
+ /// Builds the application configuration for the service.
+ ///
+ ///
+ /// An object that is used to build the configuration.
+ ///
+ protected override void BuildConfiguration(IConfigurationBuilder configurationBuilder)
+ {
+ try
+ {
+ configurationBuilder.SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");
+ }
+ finally
+ {
+ base.BuildConfiguration(configurationBuilder);
+ }
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Performs startup operations for the service.
+ ///
+ ///
+ /// A scope that is used to resolve service dependencies.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// An object that provides control over execution lifetime.
+ ///
+ protected override void OnExecutionStarting(IDependencyScope dependencyScope, IConfiguration applicationConfiguration, IServiceExecutionLifetime executionLifetime)
+ {
+ try
+ {
+ TestServiceBusConnectivity(dependencyScope);
+ MigrateDatabaseSchema(dependencyScope);
+ }
+ finally
+ {
+ base.OnExecutionStarting(dependencyScope, applicationConfiguration, executionLifetime);
+ }
+ }
+
+ ///
+ /// Adds command message listeners to the service.
+ ///
+ ///
+ /// An object that is used to add listeners.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime, if any.
+ ///
+ [DebuggerHidden]
+ private static void AddCommandListeners(IMessageListeningProfile listeningProfile, IConfiguration applicationConfiguration, String[] commandLineArguments)
+ {
+ return;
+ }
+
+ ///
+ /// Adds event message listeners to the service.
+ ///
+ ///
+ /// An object that is used to add listeners.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime, if any.
+ ///
+ [DebuggerHidden]
+ private static void AddEventListeners(IMessageListeningProfile listeningProfile, IConfiguration applicationConfiguration, String[] commandLineArguments)
+ {
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ listeningProfile.AddEventListener();
+ }
+
+ ///
+ /// Adds standard message listeners to the service.
+ ///
+ ///
+ /// An object that is used to add listeners.
+ ///
+ ///
+ /// Configuration information for the service application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime, if any.
+ ///
+ [DebuggerHidden]
+ private static void AddStandardListeners(IMessageListeningProfile listeningProfile, IConfiguration applicationConfiguration, String[] commandLineArguments)
+ {
+ return;
+ }
+
+ ///
+ /// Creates or updates the database schema.
+ ///
+ ///
+ /// A scope that is used to resolve service dependencies.
+ ///
+ [DebuggerHidden]
+ private static void MigrateDatabaseSchema(IDependencyScope dependencyScope)
+ {
+ var databaseContext = dependencyScope.Resolve();
+ databaseContext.Database.EnsureCreated();
+ Pause();
+ Console.WriteLine("Database schema created/updated.");
+ }
+
+ ///
+ /// Blocks the current thread for a short period to ensure synchronous execution of startup events.
+ ///
+ [DebuggerHidden]
+ private static void Pause() => Thread.Sleep(PauseDurationInMilliseconds);
+
+ ///
+ /// Raises an exception if a service bus connection is unavailable.
+ ///
+ ///
+ /// A scope that is used to resolve service dependencies.
+ ///
+ ///
+ /// The service was unable to verify service bus connectivity.
+ ///
+ [DebuggerHidden]
+ private static void TestServiceBusConnectivity(IDependencyScope dependencyScope)
+ {
+ try
+ {
+ var mediator = dependencyScope.Resolve();
+ var pingRequestCorrelationIdentifier = Guid.NewGuid();
+ var pingResponseMessage = mediator.Process(new PingRequestMessage(pingRequestCorrelationIdentifier));
+
+ if (pingResponseMessage is null || pingResponseMessage.CorrelationIdentifier != pingRequestCorrelationIdentifier)
+ {
+ throw new ServiceExectuionException("The service was unable to verify service bus connectivity.");
+ }
+ }
+ catch (CommandHandlingException exception)
+ {
+ throw new ServiceExectuionException("The service was unable to verify service bus connectivity. See inner exception.", exception);
+ }
+
+ Console.WriteLine("Service bus connectivity verified.");
+ }
+
+ ///
+ /// When overridden by a derived class, gets a copyright notice which is written to the console at the start of service
+ /// execution.
+ ///
+ protected override sealed String CopyrightNotice => "Copyright (c) RapidField LLC. All rights reserved.";
+
+ ///
+ /// When overridden by a derived class, gets a product name associated with the service which is written to the console at
+ /// the start of service execution.
+ ///
+ protected override sealed String ProductName => "Solid Instruments";
+
+ ///
+ /// Represents the number of milliseconds to wait when invoking .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const Int32 PauseDurationInMilliseconds = 1597;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/Program.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/Program.cs
new file mode 100644
index 00000000..a11acdf5
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/Program.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 System;
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.Service
+{
+ ///
+ /// Houses the entry point for the application.
+ ///
+ public static class Program
+ {
+ ///
+ /// Begins execution of the application.
+ ///
+ ///
+ /// Command line arguments that are provided at runtime.
+ ///
+ public static void Main(String[] args)
+ {
+ using var serviceExecutor = new ApplicationServiceExecutor(ServiceName);
+ serviceExecutor.Execute(args);
+ }
+
+ ///
+ /// Represents the name of the service that is hosted by this application.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private static readonly String ServiceName = $"{nameof(Identity)} Service";
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/README.md b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/README.md
new file mode 100644
index 00000000..ab99d755
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/README.md
@@ -0,0 +1,30 @@
+
+
+[![Solid Instruments](../../SolidInstruments.Logo.Color.Transparent.500w.png)](../../README.md)
+- - -
+
+# RapidField.SolidInstruments.Example.Domain.Identity.Service
+
+This document describes the purpose of the [`RapidField.SolidInstruments.Example.Domain.Identity.Service`]() project.
+
+## Purpose
+
+This project demonstrates an Identity domain service utilizing the **Solid Instruments** [data access](../../src/RapidField.SolidInstruments.DataAccess/README.md) and [messaging](../../src/RapidField.SolidInstruments.Messaging/README.md) constructs.
+
+## License
+
+[![License](https://img.shields.io/github/license/rapidfield/solid-instruments?style=flat&color=lightseagreen&label=license&logo=open-access&logoColor=lightgrey)](../../LICENSE.txt)
+
+**Solid Instruments** is [MIT-licensed](https://en.wikipedia.org/wiki/MIT_License). Review the [license terms](../../LICENSE.txt) for more information.
+
+
+
+- - -
+
+
+
+[![RapidField](../../RapidField.Logo.Color.Black.Transparent.200w.png)](https://www.rapidfield.com)
+
+###### Copyright (c) RapidField LLC. All rights reserved. "RapidField" and "Solid Instruments" are trademarks of RapidField LLC.
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/RapidField.SolidInstruments.Example.Domain.Identity.Service.csproj b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/RapidField.SolidInstruments.Example.Domain.Identity.Service.csproj
new file mode 100644
index 00000000..7097569d
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/RapidField.SolidInstruments.Example.Domain.Identity.Service.csproj
@@ -0,0 +1,42 @@
+
+
+
+
+ Solid Instruments contributors
+ RapidField
+ Copyright (c) RapidField LLC. All rights reserved.
+ Solid Instruments
+ This project demonstrates an Identity domain service application utilizing Solid Instruments constructs.
+ $(BuildVersion)
+ Exe
+ netcoreapp3.1
+ latest
+
+
+ bin\Debug\netcoreapp3.1\RapidField.SolidInstruments.Example.Domain.Identity.Service.xml
+ true
+
+
+
+ bin\Release\netcoreapp3.1\RapidField.SolidInstruments.Example.Domain.Identity.Service.xml
+ true
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/appsettings.json b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/appsettings.json
new file mode 100644
index 00000000..331ee9f6
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity.Service/appsettings.json
@@ -0,0 +1,16 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+{
+ "ConnectionStrings": {
+ "ExampleServiceBus": "amqp://guest:guest@localhost:5672",
+ "Identity": "Server=(LocalDB)\\MSSQLLocalDB;Database=Identity;Trusted_Connection=True;"
+ },
+ "Logging": {
+ "IncludeScopes": false,
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/DatabaseContext.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/DatabaseContext.cs
new file mode 100644
index 00000000..da6ccc9a
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/DatabaseContext.cs
@@ -0,0 +1,495 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
+using Microsoft.Azure.Cosmos.Linq;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.Extensions.Configuration;
+using RapidField.SolidInstruments.Core;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Extensions;
+using RapidField.SolidInstruments.DataAccess.EntityFramework;
+using System;
+using System.Configuration;
+using System.Data.Common;
+using System.Diagnostics;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity
+{
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ public sealed class DatabaseContext : IdentityDbContext
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// The constructor was unable to determine the appropriate connection type by evaluating the connection string.
+ ///
+ public DatabaseContext(IConfiguration applicationConfiguration)
+ : this(applicationConfiguration, DefaultDatabaseType, DefaultDatabaseName)
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// The database type of the backing database, or to determine the connection
+ /// type dynamically based on the format of the connection string.
+ ///
+ ///
+ /// The name of the backing database, which matches the associated connection string key in
+ /// . The default value is equal to the context's type name with "Context"
+ /// trimmed from the end, if found.
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// is equal to and the constructor was
+ /// unable to determine the appropriate connection type by evaluating the connection string.
+ ///
+ [DebuggerHidden]
+ private DatabaseContext(IConfiguration applicationConfiguration, ContextDatabaseType databaseType, String databaseName)
+ : this(applicationConfiguration, databaseType, databaseName ?? UseConventionalDatabaseNameIndicator, DefaultTrackingBehavior)
+ {
+ return;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// The database type of the backing database, or to determine the connection
+ /// type dynamically based on the format of the connection string.
+ ///
+ ///
+ /// The name of the backing database, which matches the associated connection string key in
+ /// . The default value is equal to the context's type name with "Context"
+ /// trimmed from the end, if found.
+ ///
+ ///
+ /// The query result tracking behavior for the context. The default value is .
+ ///
+ ///
+ /// is empty.
+ ///
+ ///
+ /// is .
+ ///
+ ///
+ /// is equal to and the constructor was
+ /// unable to determine the appropriate connection type by evaluating the connection string.
+ ///
+ [DebuggerHidden]
+ private DatabaseContext(IConfiguration applicationConfiguration, ContextDatabaseType databaseType, String databaseName, QueryTrackingBehavior trackingBehavior)
+ : base()
+ {
+ ApplicationConfiguration = applicationConfiguration.RejectIf().IsNull(nameof(applicationConfiguration)).TargetArgument;
+ DatabaseNameReference = (databaseName ?? UseConventionalDatabaseNameIndicator).RejectIf().IsNullOrEmpty(nameof(databaseName));
+ DatabaseType = databaseType == ContextDatabaseType.Unspecified ? DetermineDatabaseType(ConnectionString) : databaseType;
+ TrackingBehavior = trackingBehavior;
+
+ if (DatabaseType == ContextDatabaseType.Unspecified)
+ {
+ throw new ConfigurationErrorsException($"The identity context was unable to determine the database connection type using the connection string.");
+ }
+ }
+
+ ///
+ /// Configures the database to be used for this context.
+ ///
+ ///
+ /// A builder used to create or modify options for this context.
+ ///
+ protected sealed override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+ {
+ try
+ {
+ optionsBuilder = DatabaseType switch
+ {
+ ContextDatabaseType.Cosmos => UseCosmos(optionsBuilder),
+ ContextDatabaseType.InMemory => UseInMemory(optionsBuilder),
+ ContextDatabaseType.SqlServer => UseSqlServer(optionsBuilder),
+ _ => throw new UnsupportedSpecificationException($"The specified database type, {DatabaseType}, is not supported.")
+ };
+
+ optionsBuilder.UseQueryTrackingBehavior(TrackingBehavior);
+ }
+ finally
+ {
+ base.OnConfiguring(optionsBuilder);
+ }
+ }
+
+ ///
+ /// Configures the model that is discovered by convention from entity types exposed via
+ /// properties on the derived context.
+ ///
+ ///
+ /// A builder that is used to configure the model.
+ ///
+ protected sealed override void OnModelCreating(ModelBuilder modelBuilder)
+ {
+ try
+ {
+ OnModelCreating(ApplicationConfiguration, modelBuilder);
+ }
+ finally
+ {
+ base.OnModelCreating(modelBuilder);
+ }
+ }
+
+ ///
+ /// Determines the connection type for the specified database connection string.
+ ///
+ ///
+ /// The connection string to evaluate.
+ ///
+ ///
+ /// The resulting database connection type, or if the connection type cannot
+ /// be determined.
+ ///
+ [DebuggerHidden]
+ private static ContextDatabaseType DetermineDatabaseType(String connectionString)
+ {
+ var processedConnectionString = connectionString?.Trim().ToLower();
+
+ if (processedConnectionString.IsNullOrEmpty())
+ {
+ return ContextDatabaseType.Unspecified;
+ }
+ else if (processedConnectionString == InMemoryConnectionStringValue.ToLower())
+ {
+ return ContextDatabaseType.InMemory;
+ }
+ else if (processedConnectionString.Contains(ConnectionStringKeywordForAccountEndpoint.ToLower()) && processedConnectionString.Contains(ConnectionStringKeywordForAccountKey.ToLower()))
+ {
+ return ContextDatabaseType.Cosmos;
+ }
+ else if (processedConnectionString.Contains(ConnectionStringKeywordForDatabase.ToLower()) && processedConnectionString.Contains(ConnectionStringKeywordForServer.ToLower()))
+ {
+ return ContextDatabaseType.SqlServer;
+ }
+
+ return ContextDatabaseType.Unspecified;
+ }
+
+ ///
+ /// Returns the conventional, default name of the backing database for the specified type.
+ ///
+ ///
+ /// The type of the derived class.
+ ///
+ ///
+ /// is .
+ ///
+ [DebuggerHidden]
+ private static String GetConventionalDatabaseName(Type contextType)
+ {
+ var contextTypeName = contextType.RejectIf().IsNull(nameof(contextType)).TargetArgument.Name;
+
+ if (contextTypeName == ContextPostfixStringElement)
+ {
+ return contextTypeName;
+ }
+ else if (contextTypeName.EndsWith(ContextPostfixStringElement))
+ {
+ return contextTypeName.Substring(0, contextTypeName.Length - ContextPostfixStringElement.Length);
+ }
+
+ return contextTypeName;
+ }
+
+ ///
+ /// Configures the Cosmos DB database to be used for this context.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// A builder that is used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private static void OnConfiguringCosmos(IConfiguration applicationConfiguration, CosmosDbContextOptionsBuilder optionsBuilder)
+ {
+ return;
+ }
+
+ ///
+ /// Configures the in-memory database to be used for this context.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// A builder that is used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private static void OnConfiguringInMemory(IConfiguration applicationConfiguration, InMemoryDbContextOptionsBuilder optionsBuilder)
+ {
+ return;
+ }
+
+ ///
+ /// Configures the SQL Server database to be used for this context.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// A builder that is used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private static void OnConfiguringSqlServer(IConfiguration applicationConfiguration, SqlServerDbContextOptionsBuilder optionsBuilder)
+ {
+ return;
+ }
+
+ ///
+ /// Returns the conventional, default name of the backing database for the current .
+ ///
+ [DebuggerHidden]
+ private String GetConventionalDatabaseName() => GetConventionalDatabaseName(GetType());
+
+ ///
+ /// Configures the Cosmos DB database to be used for this context.
+ ///
+ ///
+ /// A builder used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private void OnConfiguringCosmos(CosmosDbContextOptionsBuilder optionsBuilder) => OnConfiguringCosmos(ApplicationConfiguration, optionsBuilder);
+
+ ///
+ /// Configures the in-memory database to be used for this context.
+ ///
+ ///
+ /// A builder used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private void OnConfiguringInMemory(InMemoryDbContextOptionsBuilder optionsBuilder) => OnConfiguringInMemory(ApplicationConfiguration, optionsBuilder);
+
+ ///
+ /// Configures the SQL Server database to be used for this context.
+ ///
+ ///
+ /// A builder used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private void OnConfiguringSqlServer(SqlServerDbContextOptionsBuilder optionsBuilder) => OnConfiguringSqlServer(ApplicationConfiguration, optionsBuilder);
+
+ ///
+ /// Configures the model that is discovered by convention from entity types exposed via
+ /// properties on the derived context.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// A builder that is used to configure the model.
+ ///
+ [DebuggerHidden]
+ private void OnModelCreating(IConfiguration applicationConfiguration, ModelBuilder modelBuilder)
+ {
+ return;
+ }
+
+ ///
+ /// Configures the specified options builder for a Cosmos DB database connection.
+ ///
+ ///
+ /// A builder used to create or modify options for this context.
+ ///
+ ///
+ /// The connection string is invalid.
+ ///
+ [DebuggerHidden]
+ private DbContextOptionsBuilder UseCosmos(DbContextOptionsBuilder optionsBuilder)
+ {
+ var connectionStringBuilder = new DbConnectionStringBuilder();
+ var accountEndpoint = (String)null;
+ var accountKey = (String)null;
+
+ try
+ {
+ connectionStringBuilder.ConnectionString = ConnectionString;
+
+ if (connectionStringBuilder.TryGetValue(ConnectionStringKeywordForAccountEndpoint, out var configuredAccountEndpoint))
+ {
+ accountEndpoint = configuredAccountEndpoint?.ToString().Trim();
+ }
+
+ if (accountEndpoint.IsNullOrEmpty())
+ {
+ throw new ConfigurationErrorsException($"A value for the keyword \"{ConnectionStringKeywordForAccountEndpoint}\" was not found within the connection string value for \"{DatabaseName}\".");
+ }
+
+ if (connectionStringBuilder.TryGetValue(ConnectionStringKeywordForAccountKey, out var configuredAccountKey))
+ {
+ accountKey = configuredAccountKey?.ToString().Trim();
+ }
+
+ if (accountKey.IsNullOrEmpty())
+ {
+ throw new ConfigurationErrorsException($"A value for the keyword \"{ConnectionStringKeywordForAccountKey}\" was not found within the connection string value for \"{DatabaseName}\".");
+ }
+ }
+ catch (Exception exception)
+ {
+ throw new ConfigurationErrorsException($"The connection string value for \"{DatabaseName}\" is invalid. See inner exception.", exception);
+ };
+
+ return optionsBuilder.UseCosmos(accountEndpoint, accountKey, DatabaseName, OnConfiguringCosmos);
+ }
+
+ ///
+ /// Configures the specified options builder for an in-memory database connection.
+ ///
+ ///
+ /// A builder used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private DbContextOptionsBuilder UseInMemory(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseInMemoryDatabase(DatabaseName, OnConfiguringInMemory).ConfigureWarnings((warningsBuilder) => { warningsBuilder.Ignore(InMemoryEventId.TransactionIgnoredWarning); });
+
+ ///
+ /// Configures the specified options builder for a SQL Server database connection.
+ ///
+ ///
+ /// A builder used to create or modify options for this context.
+ ///
+ [DebuggerHidden]
+ private DbContextOptionsBuilder UseSqlServer(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(ConnectionString, OnConfiguringSqlServer);
+
+ ///
+ /// Gets the connection string for the associated database from .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private String ConnectionString => ApplicationConfiguration.GetConnectionString(DatabaseName);
+
+ ///
+ /// Gets the name of the backing database, which matches the associated connection string key in
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private String DatabaseName
+ {
+ get
+ {
+ if (DatabaseNameReference == UseConventionalDatabaseNameIndicator)
+ {
+ DatabaseNameReference = GetConventionalDatabaseName();
+ }
+
+ return DatabaseNameReference;
+ }
+ }
+
+ ///
+ /// Represents the connection string keyword "AccountEndpoint".
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String ConnectionStringKeywordForAccountEndpoint = "AccountEndpoint";
+
+ ///
+ /// Represents the connection string keyword "AccountKey".
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String ConnectionStringKeywordForAccountKey = "AccountKey";
+
+ ///
+ /// Represents the connection string keyword "Database".
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String ConnectionStringKeywordForDatabase = "Database";
+
+ ///
+ /// Represents the connection string keyword "Server".
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String ConnectionStringKeywordForServer = "Server";
+
+ ///
+ /// Represents the word "Context" as a string.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String ContextPostfixStringElement = "Context";
+
+ ///
+ /// Represents the database name that is used when creating and connecting to the data source.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String DefaultDatabaseName = "Identity";
+
+ ///
+ /// Represents the default database type for backing databases.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const ContextDatabaseType DefaultDatabaseType = ContextDatabaseType.Unspecified;
+
+ ///
+ /// Represents the default query result tracking behavior for contexts.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const QueryTrackingBehavior DefaultTrackingBehavior = QueryTrackingBehavior.TrackAll;
+
+ ///
+ /// Represents a connection string value that instructs the context to use an in-memory database connection.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String InMemoryConnectionStringValue = "InMemory";
+
+ ///
+ /// Represents a value that indicates that the conventional, default database name should be used
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private const String UseConventionalDatabaseNameIndicator = "__UseConventionalDatabaseName__";
+
+ ///
+ /// Represents configuration information for the application.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly IConfiguration ApplicationConfiguration;
+
+ ///
+ /// Represents the database type of the backing database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly ContextDatabaseType DatabaseType;
+
+ ///
+ /// Represents the query result tracking behavior for the context.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly QueryTrackingBehavior TrackingBehavior;
+
+ ///
+ /// Represents the name of the backing database, which matches the associated connection string key in
+ /// .
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private String DatabaseNameReference;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/DatabaseContextDependencyModule.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/DatabaseContextDependencyModule.cs
new file mode 100644
index 00000000..0a2bfdda
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/DatabaseContextDependencyModule.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 Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using RapidField.SolidInstruments.InversionOfControl.DotNetNative;
+using RapidField.SolidInstruments.InversionOfControl.DotNetNative.Extensions;
+using System;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity
+{
+ ///
+ /// Encapsulates container configuration for the Identity database connection and related data access dependencies.
+ ///
+ public sealed class DatabaseContextDependencyModule : DotNetNativeDependencyModule
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// is .
+ ///
+ public DatabaseContextDependencyModule(IConfiguration applicationConfiguration)
+ : base(applicationConfiguration)
+ {
+ return;
+ }
+
+ ///
+ /// Configures the module.
+ ///
+ ///
+ /// An object that configures containers.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ protected override void Configure(ServiceCollection configurator, IConfiguration applicationConfiguration)
+ {
+ configurator.AddApplicationConfiguration(applicationConfiguration);
+ configurator.AddDbContext();
+ }
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlerModule.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlerModule.cs
new file mode 100644
index 00000000..11953b2b
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlerModule.cs
@@ -0,0 +1,31 @@
+// =================================================================================================================================
+// 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.EventAuthoring.DotNetNative;
+using System;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity
+{
+ ///
+ /// Encapsulates container configuration for Identity domain event handlers.
+ ///
+ public sealed class EventHandlerModule : DotNetNativeEventHandlerModule
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// Configuration information for the application.
+ ///
+ ///
+ /// is .
+ ///
+ public EventHandlerModule(IConfiguration applicationConfiguration)
+ : base(applicationConfiguration)
+ {
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelCreatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelCreatedEventHandler.cs
new file mode 100644
index 00000000..8bb86583
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelCreatedEventHandler.cs
@@ -0,0 +1,95 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.AspNetCore.Identity;
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.User.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.User.DomainModelCreatedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.User
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelCreatedEventHandler : DomainModelCreatedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelCreatedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityUser = new IdentityUser()
+ {
+ AccessFailedCount = 0,
+ Email = model.EmailAddress,
+ EmailConfirmed = false,
+ Id = model.Identifier.ToString(),
+ PasswordHash = model.PasswordHash,
+ TwoFactorEnabled = false,
+ UserName = model.Name
+ };
+
+ if (DatabaseContext.Users.Find(identityUser.Id) is null)
+ {
+ DatabaseContext.Users.Add(identityUser);
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelDeletedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelDeletedEventHandler.cs
new file mode 100644
index 00000000..57137c10
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelDeletedEventHandler.cs
@@ -0,0 +1,87 @@
+// =================================================================================================================================
+// 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.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.User.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.User.DomainModelDeletedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.User
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelDeletedEventHandler : DomainModelDeletedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelDeletedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityUser = DatabaseContext.Users.Find(model.Identifier.ToString());
+
+ if (identityUser is null)
+ {
+ return;
+ }
+
+ DatabaseContext.Users.Remove(identityUser);
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelUpdatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelUpdatedEventHandler.cs
new file mode 100644
index 00000000..e3abaf50
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/User/DomainModelUpdatedEventHandler.cs
@@ -0,0 +1,104 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.AspNetCore.Identity;
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.User.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.User.DomainModelUpdatedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.User
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelUpdatedEventHandler : DomainModelUpdatedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelUpdatedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityUser = DatabaseContext.Users.Find(model.Identifier.ToString());
+
+ if (identityUser is null)
+ {
+ identityUser = new IdentityUser()
+ {
+ AccessFailedCount = 0,
+ Email = model.EmailAddress,
+ EmailConfirmed = false,
+ Id = model.Identifier.ToString(),
+ PasswordHash = model.PasswordHash,
+ TwoFactorEnabled = false,
+ UserName = model.Name
+ };
+ DatabaseContext.Users.Add(identityUser);
+ }
+ else
+ {
+ identityUser.Email = model.EmailAddress;
+ identityUser.PasswordHash = model.PasswordHash;
+ identityUser.UserName = model.Name;
+ DatabaseContext.Users.Update(identityUser);
+ }
+
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelCreatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelCreatedEventHandler.cs
new file mode 100644
index 00000000..957303c2
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelCreatedEventHandler.cs
@@ -0,0 +1,90 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.AspNetCore.Identity;
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.UserRole.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.UserRole.DomainModelCreatedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.UserRole
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelCreatedEventHandler : DomainModelCreatedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelCreatedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityRole = new IdentityRole()
+ {
+ Id = model.Identifier.ToString(),
+ Name = model.Name
+ };
+
+ if (DatabaseContext.Roles.Find(identityRole.Id) is null)
+ {
+ DatabaseContext.Roles.Add(identityRole);
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelDeletedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelDeletedEventHandler.cs
new file mode 100644
index 00000000..e3d73597
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelDeletedEventHandler.cs
@@ -0,0 +1,87 @@
+// =================================================================================================================================
+// 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.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.UserRole.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.UserRole.DomainModelDeletedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.UserRole
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelDeletedEventHandler : DomainModelDeletedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelDeletedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityRole = DatabaseContext.Roles.Find(model.Identifier.ToString());
+
+ if (identityRole is null)
+ {
+ return;
+ }
+
+ DatabaseContext.Roles.Remove(identityRole);
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelUpdatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelUpdatedEventHandler.cs
new file mode 100644
index 00000000..a0c94ba7
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRole/DomainModelUpdatedEventHandler.cs
@@ -0,0 +1,97 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.AspNetCore.Identity;
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.UserRole.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.UserRole.DomainModelUpdatedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.UserRole
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelUpdatedEventHandler : DomainModelUpdatedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelUpdatedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityRole = DatabaseContext.Roles.Find(model.Identifier.ToString());
+
+ if (identityRole is null)
+ {
+ identityRole = new IdentityRole()
+ {
+ Id = model.Identifier.ToString(),
+ Name = model.Name
+ };
+ DatabaseContext.Roles.Add(identityRole);
+ }
+ else
+ {
+ identityRole.Name = model.Name;
+ DatabaseContext.Roles.Update(identityRole);
+ }
+
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelCreatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelCreatedEventHandler.cs
new file mode 100644
index 00000000..5df69aac
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelCreatedEventHandler.cs
@@ -0,0 +1,90 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.AspNetCore.Identity;
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.UserRoleAssignment.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.UserRoleAssignment.DomainModelCreatedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.UserRoleAssignment
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelCreatedEventHandler : DomainModelCreatedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelCreatedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityUserRole = new IdentityUserRole()
+ {
+ RoleId = model.UserRoleIdentifier.ToString(),
+ UserId = model.UserIdentifier.ToString()
+ };
+
+ if (DatabaseContext.UserRoles.Find(identityUserRole.UserId, identityUserRole.RoleId) is null)
+ {
+ DatabaseContext.UserRoles.Add(identityUserRole);
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelDeletedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelDeletedEventHandler.cs
new file mode 100644
index 00000000..5fc0458e
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelDeletedEventHandler.cs
@@ -0,0 +1,87 @@
+// =================================================================================================================================
+// 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.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.UserRoleAssignment.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.UserRoleAssignment.DomainModelDeletedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.UserRoleAssignment
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelDeletedEventHandler : DomainModelDeletedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelDeletedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityUserRole = DatabaseContext.UserRoles.Find(model.UserIdentifier.ToString(), model.UserRoleIdentifier.ToString());
+
+ if (identityUserRole is null)
+ {
+ return;
+ }
+
+ DatabaseContext.UserRoles.Remove(identityUserRole);
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelUpdatedEventHandler.cs b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelUpdatedEventHandler.cs
new file mode 100644
index 00000000..1e513fa8
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/EventHandlers/ModelState/UserRoleAssignment/DomainModelUpdatedEventHandler.cs
@@ -0,0 +1,91 @@
+// =================================================================================================================================
+// Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in the project root for license information.
+// =================================================================================================================================
+
+using Microsoft.AspNetCore.Identity;
+using RapidField.SolidInstruments.Command;
+using RapidField.SolidInstruments.Core.ArgumentValidation;
+using RapidField.SolidInstruments.Core.Concurrency;
+using RapidField.SolidInstruments.EventAuthoring;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using DomainModel = RapidField.SolidInstruments.Example.Domain.Models.UserRoleAssignment.DomainModel;
+using DomainModelEvent = RapidField.SolidInstruments.Example.Domain.Events.ModelState.UserRoleAssignment.DomainModelUpdatedEvent;
+
+namespace RapidField.SolidInstruments.Example.Domain.Identity.EventHandlers.ModelState.UserRoleAssignment
+{
+ ///
+ /// Processes a single .
+ ///
+ public sealed class DomainModelUpdatedEventHandler : DomainModelUpdatedEventHandler
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A connection to the Identity database.
+ ///
+ ///
+ /// is -or- is
+ /// .
+ ///
+ public DomainModelUpdatedEventHandler(ICommandMediator mediator, DatabaseContext databaseContext)
+ : base(mediator)
+ {
+ DatabaseContext = databaseContext.RejectIf().IsNull(nameof(databaseContext));
+ }
+
+ ///
+ /// Releases all resources consumed by the current .
+ ///
+ ///
+ /// A value indicating whether or not managed resources should be released.
+ ///
+ protected override void Dispose(Boolean disposing) => base.Dispose(disposing);
+
+ ///
+ /// Processes the specified domain model.
+ ///
+ ///
+ /// The model that was created.
+ ///
+ ///
+ /// A collection of textual labels that provide categorical and/or contextual information about the event.
+ ///
+ ///
+ /// A unique identifier to assign to sub-commands.
+ ///
+ ///
+ /// A processing intermediary that is used to process sub-commands.
+ ///
+ ///
+ /// A token that represents and manages contextual thread safety.
+ ///
+ protected override void Process(DomainModel model, IEnumerable labels, Guid correlationIdentifier, ICommandMediator mediator, IConcurrencyControlToken controlToken)
+ {
+ var identityUserRole = DatabaseContext.UserRoles.Find(model.UserIdentifier.ToString(), model.UserRoleIdentifier.ToString());
+
+ if (identityUserRole is null)
+ {
+ identityUserRole = new IdentityUserRole()
+ {
+ RoleId = model.UserRoleIdentifier.ToString(),
+ UserId = model.UserIdentifier.ToString()
+ };
+ DatabaseContext.UserRoles.Add(identityUserRole);
+ DatabaseContext.SaveChanges();
+ Console.WriteLine($"{DomainModelEvent.DataContractNameVerb} {DomainModel.DataContractName} {model.Identifier}.");
+ }
+ }
+
+ ///
+ /// Represents a connection to the Identity database.
+ ///
+ [DebuggerBrowsable(DebuggerBrowsableState.Never)]
+ private readonly DatabaseContext DatabaseContext;
+ }
+}
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/README.md b/example/RapidField.SolidInstruments.Example.Domain.Identity/README.md
new file mode 100644
index 00000000..f9a6099d
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/README.md
@@ -0,0 +1,30 @@
+
+
+[![Solid Instruments](../../SolidInstruments.Logo.Color.Transparent.500w.png)](../../README.md)
+- - -
+
+# RapidField.SolidInstruments.Example.Domain.Identity
+
+This document describes the purpose of the [`RapidField.SolidInstruments.Example.Domain.Identity`]() project.
+
+## Purpose
+
+This project demonstrates Identity domain logic utilizing the **Solid Instruments** [data access](../../src/RapidField.SolidInstruments.DataAccess/README.md) constructs.
+
+## License
+
+[![License](https://img.shields.io/github/license/rapidfield/solid-instruments?style=flat&color=lightseagreen&label=license&logo=open-access&logoColor=lightgrey)](../../LICENSE.txt)
+
+**Solid Instruments** is [MIT-licensed](https://en.wikipedia.org/wiki/MIT_License). Review the [license terms](../../LICENSE.txt) for more information.
+
+
+
+- - -
+
+
+
+[![RapidField](../../RapidField.Logo.Color.Black.Transparent.200w.png)](https://www.rapidfield.com)
+
+###### Copyright (c) RapidField LLC. All rights reserved. "RapidField" and "Solid Instruments" are trademarks of RapidField LLC.
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain.Identity/RapidField.SolidInstruments.Example.Domain.Identity.csproj b/example/RapidField.SolidInstruments.Example.Domain.Identity/RapidField.SolidInstruments.Example.Domain.Identity.csproj
new file mode 100644
index 00000000..93803446
--- /dev/null
+++ b/example/RapidField.SolidInstruments.Example.Domain.Identity/RapidField.SolidInstruments.Example.Domain.Identity.csproj
@@ -0,0 +1,38 @@
+
+
+
+
+ Solid Instruments contributors
+ RapidField
+ Copyright (c) RapidField LLC. All rights reserved.
+ Solid Instruments
+ This project demonstrates Identity domain logic utilizing the Solid Instruments data access and messaging constructs.
+ $(BuildVersion)
+ netstandard2.1
+ latest
+
+
+ bin\Debug\netstandard2.1\RapidField.SolidInstruments.Example.Domain.Identity.xml
+ true
+
+
+
+ bin\Release\netstandard2.1\RapidField.SolidInstruments.Example.Domain.Identity.xml
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Named.cs b/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Named.cs
index cbc96fac..7808a817 100644
--- a/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Named.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Named.cs
@@ -74,7 +74,7 @@ private static String GetPasswordHash(String plaintextPassword)
public static DomainModel StevenCallahan => new DomainModel(Guid.Parse("a04fc5b0-1a67-43ff-89af-750128398d8a"))
{
EmailAddress = "steven.callahan@example.com",
- Name = "Steven Callahan",
+ Name = nameof(StevenCallahan),
PasswordHash = GetPasswordHash("My name is Steven. 321")
};
@@ -84,7 +84,7 @@ private static String GetPasswordHash(String plaintextPassword)
public static DomainModel TomSmith => new DomainModel(Guid.Parse("3d46470a-1c90-4e94-bc5c-3cbde44ba6ac"))
{
EmailAddress = "tom.smith@example.com",
- Name = "Tom Smith",
+ Name = nameof(TomSmith),
PasswordHash = GetPasswordHash("My name is Tom. 321")
};
}
diff --git a/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Navigation.cs b/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Navigation.cs
index 934a4626..49bf0a25 100644
--- a/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Navigation.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain/Models/User/DomainModel.Navigation.cs
@@ -21,8 +21,8 @@ namespace RapidField.SolidInstruments.Example.Domain.Models.User
/// - DO declare private standard navigation fields as concrete domain models.
/// - DO declare internal inverse collection navigation properties as of concrete domain models.
/// - DO decorate (public and internal) standard navigation properties with .
- /// - DO decorate public inverse collection navigation properties with .
- /// - DO decorate internal inverse collection navigation properties with .
+ /// - DO decorate public inverse collection navigation properties with .
+ /// - DO decorate internal inverse collection navigation properties with .
/// - DO expose getters and internal setters for public standard navigation properties.
/// - DO expose getters (but not setters) for (public and internal) inverse collection navigation properties.
/// - DO lazily initialize inverse navigation collections.
diff --git a/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Named.cs b/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Named.cs
index a071c1bb..b43e8f29 100644
--- a/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Named.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Named.cs
@@ -49,7 +49,7 @@ public static class Named
public static DomainModel EndUser => new DomainModel(Guid.Parse("816e7126-2034-49b3-af08-d07cab150d93"))
{
Description = "A standard end user.",
- Name = "End User"
+ Name = nameof(EndUser)
};
///
@@ -58,7 +58,7 @@ public static class Named
public static DomainModel SystemAdministrator => new DomainModel(Guid.Parse("b13c7a39-0c65-4515-a4af-2e3f60d289ba"))
{
Description = "A user with full administrative privileges.",
- Name = "System Administrator"
+ Name = nameof(SystemAdministrator)
};
}
}
diff --git a/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Navigation.cs b/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Navigation.cs
index ada87627..313a6355 100644
--- a/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Navigation.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain/Models/UserRole/DomainModel.Navigation.cs
@@ -19,8 +19,8 @@ namespace RapidField.SolidInstruments.Example.Domain.Models.UserRole
/// - DO declare private standard navigation fields as concrete domain models.
/// - DO declare internal inverse collection navigation properties as of concrete domain models.
/// - DO decorate (public and internal) standard navigation properties with .
- /// - DO decorate public inverse collection navigation properties with .
- /// - DO decorate internal inverse collection navigation properties with .
+ /// - DO decorate public inverse collection navigation properties with .
+ /// - DO decorate internal inverse collection navigation properties with .
/// - DO expose getters and internal setters for public standard navigation properties.
/// - DO expose getters (but not setters) for (public and internal) inverse collection navigation properties.
/// - DO lazily initialize inverse navigation collections.
diff --git a/example/RapidField.SolidInstruments.Example.Domain/Models/UserRoleAssignment/DomainModel.Navigation.cs b/example/RapidField.SolidInstruments.Example.Domain/Models/UserRoleAssignment/DomainModel.Navigation.cs
index 484287b6..58afe654 100644
--- a/example/RapidField.SolidInstruments.Example.Domain/Models/UserRoleAssignment/DomainModel.Navigation.cs
+++ b/example/RapidField.SolidInstruments.Example.Domain/Models/UserRoleAssignment/DomainModel.Navigation.cs
@@ -24,8 +24,8 @@ namespace RapidField.SolidInstruments.Example.Domain.Models.UserRoleAssignment
/// - DO declare private standard navigation fields as concrete domain models.
/// - DO declare internal inverse collection navigation properties as of concrete domain models.
/// - DO decorate (public and internal) standard navigation properties with .
- /// - DO decorate public inverse collection navigation properties with .
- /// - DO decorate internal inverse collection navigation properties with .
+ /// - DO decorate public inverse collection navigation properties with .
+ /// - DO decorate internal inverse collection navigation properties with .
/// - DO expose getters and internal setters for public standard navigation properties.
/// - DO expose getters (but not setters) for (public and internal) inverse collection navigation properties.
/// - DO lazily initialize inverse navigation collections.
diff --git a/psakefile.ps1 b/psakefile.ps1
index 16c55123..ed00c38c 100644
--- a/psakefile.ps1
+++ b/psakefile.ps1
@@ -39,12 +39,16 @@ Task Clean-Debug -Alias cd -Action { CleanDebug; };
Task Clean-Release -Alias cr -Action { CleanRelease; };
Task List -Alias l -Action { psake -docs };
Task Restore-Dependencies -Alias rd -Action { RestoreDependencies; };
-Task Start-All-Debug -Alias sad -Depends Stop-All, Start-ExampleBeaconServiceApplication-Debug, Start-ExampleAccessControlServiceApplication-Debug;
-Task Start-All-Release -Alias sar -Depends Stop-All, Start-ExampleBeaconServiceApplication-Release, Start-ExampleAccessControlServiceApplication-Release;
+Task Start-All-Debug -Alias sad -Depends Stop-All, Start-ExampleBeaconServiceApplication-Debug, Start-ExampleIdentityServiceApplication-Debug, Start-ExampleAccessControlServiceApplication-Debug, Start-ExampleAccessControlHttpApiApplication-Debug;
+Task Start-All-Release -Alias sar -Depends Stop-All, Start-ExampleBeaconServiceApplication-Release, Start-ExampleIdentityServiceApplication-Release, Start-ExampleAccessControlServiceApplication-Release, Start-ExampleAccessControlHttpApiApplication-Release;
+Task Start-ExampleAccessControlHttpApiApplication-Debug -Alias seachad -Depends Build-Debug -Action { StartExampleAccessControlHttpApiApplicationDebug; };
+Task Start-ExampleAccessControlHttpApiApplication-Release -Alias seachar -Depends Build-Release -Action { StartExampleAccessControlHttpApiApplicationRelease; };
Task Start-ExampleAccessControlServiceApplication-Debug -Alias seacsad -Depends Build-Debug -Action { StartExampleAccessControlServiceApplicationDebug; };
Task Start-ExampleAccessControlServiceApplication-Release -Alias seacsar -Depends Build-Release -Action { StartExampleAccessControlServiceApplicationRelease; };
Task Start-ExampleBeaconServiceApplication-Debug -Alias sebsad -Depends Build-Debug -Action { StartExampleBeaconServiceApplicationDebug; };
Task Start-ExampleBeaconServiceApplication-Release -Alias sebsar -Depends Build-Release -Action { StartExampleBeaconServiceApplicationRelease; };
+Task Start-ExampleIdentityServiceApplication-Debug -Alias seisad -Depends Build-Debug -Action { StartExampleIdentityServiceApplicationDebug; };
+Task Start-ExampleIdentityServiceApplication-Release -Alias seisar -Depends Build-Release -Action { StartExampleIdentityServiceApplicationRelease; };
Task Stop-All -Alias sa -Action { StopAllApplications; };
Task Test-All -Alias ta -Depends Build-All, Test-Debug, Test-Release;
Task Test-Debug -Alias td -Depends Build-Debug -Action { TestDebug; };
diff --git a/src/RapidField.SolidInstruments.Command.Autofac/RapidField.SolidInstruments.Command.Autofac.csproj b/src/RapidField.SolidInstruments.Command.Autofac/RapidField.SolidInstruments.Command.Autofac.csproj
index 2ab3d2c4..21cad6ab 100644
--- a/src/RapidField.SolidInstruments.Command.Autofac/RapidField.SolidInstruments.Command.Autofac.csproj
+++ b/src/RapidField.SolidInstruments.Command.Autofac/RapidField.SolidInstruments.Command.Autofac.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.Command.DotNetNative/RapidField.SolidInstruments.Command.DotNetNative.csproj b/src/RapidField.SolidInstruments.Command.DotNetNative/RapidField.SolidInstruments.Command.DotNetNative.csproj
index b94ca534..3ef4aa07 100644
--- a/src/RapidField.SolidInstruments.Command.DotNetNative/RapidField.SolidInstruments.Command.DotNetNative.csproj
+++ b/src/RapidField.SolidInstruments.Command.DotNetNative/RapidField.SolidInstruments.Command.DotNetNative.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.Core/RapidField.SolidInstruments.Core.csproj b/src/RapidField.SolidInstruments.Core/RapidField.SolidInstruments.Core.csproj
index 04b1dde7..b52bafff 100644
--- a/src/RapidField.SolidInstruments.Core/RapidField.SolidInstruments.Core.csproj
+++ b/src/RapidField.SolidInstruments.Core/RapidField.SolidInstruments.Core.csproj
@@ -38,6 +38,6 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
\ No newline at end of file
diff --git a/src/RapidField.SolidInstruments.DataAccess.Autofac.Ef/RapidField.SolidInstruments.DataAccess.Autofac.Ef.csproj b/src/RapidField.SolidInstruments.DataAccess.Autofac.Ef/RapidField.SolidInstruments.DataAccess.Autofac.Ef.csproj
index d78f3911..4f3b4c02 100644
--- a/src/RapidField.SolidInstruments.DataAccess.Autofac.Ef/RapidField.SolidInstruments.DataAccess.Autofac.Ef.csproj
+++ b/src/RapidField.SolidInstruments.DataAccess.Autofac.Ef/RapidField.SolidInstruments.DataAccess.Autofac.Ef.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.DataAccess.Autofac/RapidField.SolidInstruments.DataAccess.Autofac.csproj b/src/RapidField.SolidInstruments.DataAccess.Autofac/RapidField.SolidInstruments.DataAccess.Autofac.csproj
index 1ae845f7..10e665b4 100644
--- a/src/RapidField.SolidInstruments.DataAccess.Autofac/RapidField.SolidInstruments.DataAccess.Autofac.csproj
+++ b/src/RapidField.SolidInstruments.DataAccess.Autofac/RapidField.SolidInstruments.DataAccess.Autofac.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef.csproj b/src/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef.csproj
index eaee4f4b..7b34a94a 100644
--- a/src/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef.csproj
+++ b/src/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef/RapidField.SolidInstruments.DataAccess.DotNetNative.Ef.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.DataAccess.DotNetNative/RapidField.SolidInstruments.DataAccess.DotNetNative.csproj b/src/RapidField.SolidInstruments.DataAccess.DotNetNative/RapidField.SolidInstruments.DataAccess.DotNetNative.csproj
index fd0fa381..4ee0f159 100644
--- a/src/RapidField.SolidInstruments.DataAccess.DotNetNative/RapidField.SolidInstruments.DataAccess.DotNetNative.csproj
+++ b/src/RapidField.SolidInstruments.DataAccess.DotNetNative/RapidField.SolidInstruments.DataAccess.DotNetNative.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.DataAccess.EntityFramework/RapidField.SolidInstruments.DataAccess.EntityFramework.csproj b/src/RapidField.SolidInstruments.DataAccess.EntityFramework/RapidField.SolidInstruments.DataAccess.EntityFramework.csproj
index cb99729d..13b23168 100644
--- a/src/RapidField.SolidInstruments.DataAccess.EntityFramework/RapidField.SolidInstruments.DataAccess.EntityFramework.csproj
+++ b/src/RapidField.SolidInstruments.DataAccess.EntityFramework/RapidField.SolidInstruments.DataAccess.EntityFramework.csproj
@@ -37,10 +37,10 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
-
+
+
+
+
diff --git a/src/RapidField.SolidInstruments.EventAuthoring.Autofac/RapidField.SolidInstruments.EventAuthoring.Autofac.csproj b/src/RapidField.SolidInstruments.EventAuthoring.Autofac/RapidField.SolidInstruments.EventAuthoring.Autofac.csproj
index 06367887..b215f605 100644
--- a/src/RapidField.SolidInstruments.EventAuthoring.Autofac/RapidField.SolidInstruments.EventAuthoring.Autofac.csproj
+++ b/src/RapidField.SolidInstruments.EventAuthoring.Autofac/RapidField.SolidInstruments.EventAuthoring.Autofac.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.EventAuthoring.DotNetNative/RapidField.SolidInstruments.EventAuthoring.DotNetNative.csproj b/src/RapidField.SolidInstruments.EventAuthoring.DotNetNative/RapidField.SolidInstruments.EventAuthoring.DotNetNative.csproj
index 8625b7b7..fc2ced73 100644
--- a/src/RapidField.SolidInstruments.EventAuthoring.DotNetNative/RapidField.SolidInstruments.EventAuthoring.DotNetNative.csproj
+++ b/src/RapidField.SolidInstruments.EventAuthoring.DotNetNative/RapidField.SolidInstruments.EventAuthoring.DotNetNative.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.InversionOfControl.Autofac/RapidField.SolidInstruments.InversionOfControl.Autofac.csproj b/src/RapidField.SolidInstruments.InversionOfControl.Autofac/RapidField.SolidInstruments.InversionOfControl.Autofac.csproj
index 034a3234..348c2ec2 100644
--- a/src/RapidField.SolidInstruments.InversionOfControl.Autofac/RapidField.SolidInstruments.InversionOfControl.Autofac.csproj
+++ b/src/RapidField.SolidInstruments.InversionOfControl.Autofac/RapidField.SolidInstruments.InversionOfControl.Autofac.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.InversionOfControl.DotNetNative/RapidField.SolidInstruments.InversionOfControl.DotNetNative.csproj b/src/RapidField.SolidInstruments.InversionOfControl.DotNetNative/RapidField.SolidInstruments.InversionOfControl.DotNetNative.csproj
index bbf0b460..16e3d83a 100644
--- a/src/RapidField.SolidInstruments.InversionOfControl.DotNetNative/RapidField.SolidInstruments.InversionOfControl.DotNetNative.csproj
+++ b/src/RapidField.SolidInstruments.InversionOfControl.DotNetNative/RapidField.SolidInstruments.InversionOfControl.DotNetNative.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.InversionOfControl/RapidField.SolidInstruments.InversionOfControl.csproj b/src/RapidField.SolidInstruments.InversionOfControl/RapidField.SolidInstruments.InversionOfControl.csproj
index 6bf95926..1c08fd88 100644
--- a/src/RapidField.SolidInstruments.InversionOfControl/RapidField.SolidInstruments.InversionOfControl.csproj
+++ b/src/RapidField.SolidInstruments.InversionOfControl/RapidField.SolidInstruments.InversionOfControl.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/RapidField.SolidInstruments.Messaging.Autofac.Asb.csproj b/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/RapidField.SolidInstruments.Messaging.Autofac.Asb.csproj
index 1a0bc29d..101f9881 100644
--- a/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/RapidField.SolidInstruments.Messaging.Autofac.Asb.csproj
+++ b/src/RapidField.SolidInstruments.Messaging.Autofac.Asb/RapidField.SolidInstruments.Messaging.Autofac.Asb.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/RapidField.SolidInstruments.Messaging.Autofac.Rmq.csproj b/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/RapidField.SolidInstruments.Messaging.Autofac.Rmq.csproj
index 2f6d9967..a2f11b32 100644
--- a/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/RapidField.SolidInstruments.Messaging.Autofac.Rmq.csproj
+++ b/src/RapidField.SolidInstruments.Messaging.Autofac.Rmq/RapidField.SolidInstruments.Messaging.Autofac.Rmq.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.Messaging.Autofac/RapidField.SolidInstruments.Messaging.Autofac.csproj b/src/RapidField.SolidInstruments.Messaging.Autofac/RapidField.SolidInstruments.Messaging.Autofac.csproj
index a9cbd25e..58f88c7b 100644
--- a/src/RapidField.SolidInstruments.Messaging.Autofac/RapidField.SolidInstruments.Messaging.Autofac.csproj
+++ b/src/RapidField.SolidInstruments.Messaging.Autofac/RapidField.SolidInstruments.Messaging.Autofac.csproj
@@ -39,7 +39,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/RapidField.SolidInstruments.Messaging.DotNetNative.Asb.csproj b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/RapidField.SolidInstruments.Messaging.DotNetNative.Asb.csproj
index c41e2956..b7e23a39 100644
--- a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/RapidField.SolidInstruments.Messaging.DotNetNative.Asb.csproj
+++ b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Asb/RapidField.SolidInstruments.Messaging.DotNetNative.Asb.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.csproj b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.csproj
index b29d014c..0411d9f6 100644
--- a/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.csproj
+++ b/src/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq/RapidField.SolidInstruments.Messaging.DotNetNative.Rmq.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.Messaging.DotNetNative/RapidField.SolidInstruments.Messaging.DotNetNative.csproj b/src/RapidField.SolidInstruments.Messaging.DotNetNative/RapidField.SolidInstruments.Messaging.DotNetNative.csproj
index aa4550b2..ff3d48d9 100644
--- a/src/RapidField.SolidInstruments.Messaging.DotNetNative/RapidField.SolidInstruments.Messaging.DotNetNative.csproj
+++ b/src/RapidField.SolidInstruments.Messaging.DotNetNative/RapidField.SolidInstruments.Messaging.DotNetNative.csproj
@@ -37,9 +37,9 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
-
-
+
+
+
diff --git a/src/RapidField.SolidInstruments.ObjectComposition/RapidField.SolidInstruments.ObjectComposition.csproj b/src/RapidField.SolidInstruments.ObjectComposition/RapidField.SolidInstruments.ObjectComposition.csproj
index 32b33c70..93d4ed51 100644
--- a/src/RapidField.SolidInstruments.ObjectComposition/RapidField.SolidInstruments.ObjectComposition.csproj
+++ b/src/RapidField.SolidInstruments.ObjectComposition/RapidField.SolidInstruments.ObjectComposition.csproj
@@ -37,7 +37,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/test/RapidField.SolidInstruments.Core.UnitTests/RapidField.SolidInstruments.Core.UnitTests.csproj b/test/RapidField.SolidInstruments.Core.UnitTests/RapidField.SolidInstruments.Core.UnitTests.csproj
index ad8ec575..b7fc967a 100644
--- a/test/RapidField.SolidInstruments.Core.UnitTests/RapidField.SolidInstruments.Core.UnitTests.csproj
+++ b/test/RapidField.SolidInstruments.Core.UnitTests/RapidField.SolidInstruments.Core.UnitTests.csproj
@@ -19,7 +19,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/test/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests.csproj b/test/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests.csproj
index fd6aebc6..5648795a 100644
--- a/test/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests.csproj
+++ b/test/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests/RapidField.SolidInstruments.InversionOfControl.Autofac.UnitTests.csproj
@@ -19,7 +19,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/test/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests.csproj b/test/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests.csproj
index 36fb3bb6..57e8917f 100644
--- a/test/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests.csproj
+++ b/test/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests/RapidField.SolidInstruments.InversionOfControl.DotNetNative.UnitTests.csproj
@@ -19,7 +19,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/test/RapidField.SolidInstruments.InversionOfControl.UnitTests/RapidField.SolidInstruments.InversionOfControl.UnitTests.csproj b/test/RapidField.SolidInstruments.InversionOfControl.UnitTests/RapidField.SolidInstruments.InversionOfControl.UnitTests.csproj
index da9e830a..41c2ffc2 100644
--- a/test/RapidField.SolidInstruments.InversionOfControl.UnitTests/RapidField.SolidInstruments.InversionOfControl.UnitTests.csproj
+++ b/test/RapidField.SolidInstruments.InversionOfControl.UnitTests/RapidField.SolidInstruments.InversionOfControl.UnitTests.csproj
@@ -19,7 +19,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+
diff --git a/test/RapidField.SolidInstruments.ObjectComposition.UnitTests/RapidField.SolidInstruments.ObjectComposition.UnitTests.csproj b/test/RapidField.SolidInstruments.ObjectComposition.UnitTests/RapidField.SolidInstruments.ObjectComposition.UnitTests.csproj
index 09da7cd4..43b6b478 100644
--- a/test/RapidField.SolidInstruments.ObjectComposition.UnitTests/RapidField.SolidInstruments.ObjectComposition.UnitTests.csproj
+++ b/test/RapidField.SolidInstruments.ObjectComposition.UnitTests/RapidField.SolidInstruments.ObjectComposition.UnitTests.csproj
@@ -19,7 +19,7 @@ Copyright (c) RapidField LLC. Licensed under the MIT License. See LICENSE.txt in
-
+