diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 00000000000..bc0b17da2f8
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,39 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/dotnet
+{
+ "name": "C# (.NET)",
+ "image": "mcr.microsoft.com/devcontainers/dotnet:0-6.0",
+ "features": {
+ "ghcr.io/devcontainers/features/github-cli:1": {},
+ "ghcr.io/dhoeric/features/act:1": {},
+ "ghcr.io/devcontainers/features/dotnet:1": {
+ "version": "7"
+ }
+ },
+ "customizations": {
+ "vscode": {
+ "extensions": [
+ "ms-dotnettools.csharp",
+ "k--kato.docomment",
+ "formulahendry.dotnet-test-explorer",
+ "leo-labs.dotnet",
+ "github.vscode-pull-request-github"
+ ]
+ }
+ }
+
+ // Features to add to the dev container. More info: https://containers.dev/features.
+ // "features": {},
+
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ // "forwardPorts": [5000, 5001],
+
+ // Use 'postCreateCommand' to run commands after the container is created.
+ // "postCreateCommand": "dotnet restore",
+
+ // Configure tool-specific properties.
+ // "customizations": {},
+
+ // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
+ // "remoteUser": "root"
+}
diff --git a/.editorconfig b/.editorconfig
index 6bd1ca1b5e4..e8366c4a0f4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -15,6 +15,7 @@ indent_size = 2
[*.cs]
end_of_line = lf
+dotnet_diagnostic.NUnit1032.severity = suggestion
[*.md]
trim_trailing_whitespace = false
@@ -24,3 +25,4 @@ end_of_line = lf
[*.{cmd, bat}]
end_of_line = crlf
+
diff --git a/.gitattributes b/.gitattributes
index 68ead8618fa..6e127ce325c 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,18 +1,6 @@
# Set default behavior to automatically normalize line endings.
* text=auto
-*.doc diff=astextplain
-*.DOC diff=astextplain
-*.docx diff=astextplain
-*.DOCX diff=astextplain
-*.dot diff=astextplain
-*.DOT diff=astextplain
-*.pdf diff=astextplain
-*.PDF diff=astextplain
-*.rtf diff=astextplain
-*.RTF diff=astextplain
-*.md diff=astextplain
-
*.jpg binary
*.png binary
*.gif binary
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index f277a2db967..bdf43b8594e 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -1,5 +1,5 @@
name: Bug Report
-description: Create a bug report for an issue found in MassTransit
+description: Create a bug report for an issue found in MassTransit. Should not be used for questions on the use of MassTransit.
body:
- type: markdown
attributes:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 1b620096adc..656ec73dfd5 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,14 +1,14 @@
blank_issues_enabled: false
contact_links:
- name: Commercial Support
- url: https://masstransit-project.com/learn/support.html
- about: For commerial support services, including architectural guidance, code review, and troubleshooting
+ url: https://masstransit.io/support
+ about: Commercial support, the official support services for MassTransit customers
- name: GitHub Discussions
url: https://github.com/MassTransit/MassTransit/discussions
- about: Submit questions, ideas, or general conversation topics
- - name: Live Chat (on Discord)
+ about: Questions on the use of MassTransit, use search to find existing discussions
+ - name: Discord
url: https://discord.gg/rNpQgYn
- about: Live chat room for questions and conversation
+ about: Active chat room for MassTransit-related conversations. Should not be used for posting code snippets (use GitHub Discusssions)
- name: Stack Overflow
url: https://stackoverflow.com/questions/tagged/masstransit
- about: Questions and answers, search to see if your question has already been answered
\ No newline at end of file
+ about: Search for existing questions and answers, should not be used for asking support-related questions.
\ No newline at end of file
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f3e505f8672..6d2ab8f48f9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,8 +1,6 @@
name: MassTransit
env:
- MASSTRANSIT_VERSION: 8.0.13
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: true
+ MASSTRANSIT_VERSION: 8.3.1
on:
push:
paths:
@@ -10,6 +8,7 @@ on:
- 'tests/**'
- 'MassTransit.sln'
- 'Directory.Build.props'
+ - 'Directory.Packages.props'
- '**/build.yml'
pull_request:
paths:
@@ -17,13 +16,14 @@ on:
- 'tests/**'
- 'MassTransit.sln'
- 'Directory.Build.props'
+ - 'Directory.Packages.props'
- '**/build.yml'
workflow_dispatch:
jobs:
compile:
name: Build
- timeout-minutes: 10
+ timeout-minutes: 15
strategy:
max-parallel: 2
matrix:
@@ -31,12 +31,12 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Restore NuGet packages
run: dotnet restore
@@ -47,7 +47,7 @@ jobs:
working-directory: ./
- name: Test Analyzers
- run: dotnet test -c Release --logger:"console;verbosity=normal" --no-build --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --no-build --filter Category!=Flaky
working-directory: tests/MassTransit.Analyzers.Tests
test-ubuntu:
@@ -56,38 +56,21 @@ jobs:
timeout-minutes: 10
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Unit Tests
- run: dotnet test -c Release -f net6.0 --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release -f net8.0 --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.Tests
- name: Test Abstractions
- run: dotnet test -c Release -f net6.0 --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release -f net8.0 --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.Abstractions.Tests
- test-containers:
- name: Container Tests
- runs-on: ubuntu-latest
- timeout-minutes: 10
- steps:
- - name: Check out code
- uses: actions/checkout@v3
-
- - name: Install .NET Core SDK 6.0
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: '6.0.x'
-
- - name: Test Containers
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
- working-directory: tests/MassTransit.Containers.Tests
-
test-activemq:
name: "Transports: ActiveMQ"
timeout-minutes: 10
@@ -100,51 +83,70 @@ jobs:
- "8161:8161"
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test ActiveMQ
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.ActiveMqTransport.Tests
- test-grpc:
- name: "Transports: gRPC"
+ test-sql-transport:
+ name: "Transport: SQL"
timeout-minutes: 10
runs-on: ubuntu-latest
+ services:
+ mssql:
+ image: mcr.microsoft.com/azure-sql-edge
+ env:
+ ACCEPT_EULA: Y
+ SA_PASSWORD: "Password12!"
+ ports:
+ - 1433:1433
+ postgres:
+ image: postgres:14.7
+ env:
+ POSTGRES_PASSWORD: "Password12!"
+ ports:
+ - 5432:5432
+ options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
+ env:
+ DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: false
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
+
+ - name: Test Database Transport
+ run: dotnet test -c Release --logger GitHubActions --filter "Category!=Flaky&Category!=Integration"
+ working-directory: tests/MassTransit.SqlTransport.Tests
+
- - name: Test gRPC
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
- working-directory: tests/MassTransit.GrpcTransport.Tests
test-azure-service-bus:
name: "Transports: Azure Service Bus"
if: false # too flaky at this point
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test Azure Service Bus
env:
MT_ASB_KEYVALUE: ${{ secrets.AZURE_SERVICEBUS }}
MT_AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE }}
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.Azure.ServiceBus.Core.Tests
test-rabbitmq:
name: "Transports: RabbitMQ"
@@ -159,58 +161,66 @@ jobs:
options: --health-cmd "rabbitmqctl node_health_check" --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test RabbitMQ
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.RabbitMqTransport.Tests
test-sqs:
- name: "Transports: SQS"
+ name: "Transports: SQS (+S3)"
timeout-minutes: 10
runs-on: ubuntu-latest
services:
localstack:
- image: localstack/localstack:0.12.17.5
+ image: localstack/localstack:3.0.2
ports:
- "4566:4566"
- "4571:4571"
- - "8080:8080"
- options: --health-cmd "curl --fail http://localhost:4566/health || exit 1" --health-interval 10s --health-timeout 5s --health-retries 5
+ options: --health-cmd "curl --fail http://localhost:4566/health || exit 1" --health-interval 10s --health-timeout 5s --health-retries 5 --health-start-period 15s
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test SQS
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.AmazonSqsTransport.Tests
test-azure-table:
name: "Storage: Azure Table"
- if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/v8') && github.repository == 'MassTransit/MassTransit'
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
+
+ - name: Spin up test environment
+ run: |
+ docker compose -f docker-compose.yml up -d
+ working-directory: tests/MassTransit.Azure.Table.Tests
- name: Test Azure Table
env:
MT_AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE }}
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter "Category!=Flaky&Category!=Integration"
+ run: dotnet test -c Release --logger GitHubActions --filter "Category!=Flaky&Category!=Integration"
+ working-directory: tests/MassTransit.Azure.Table.Tests
+
+ - name: Stop test environment
+ run: |
+ docker compose -f docker-compose.yml down
working-directory: tests/MassTransit.Azure.Table.Tests
test-cosmosdb:
name: "Storage: CosmosDB"
@@ -219,18 +229,18 @@ jobs:
if: (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/v8') && github.repository == 'MassTransit/MassTransit'
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test CosmosDB
env:
MT_COSMOS_ENDPOINT: ${{ secrets.AZURE_COSMOSENDPOINT }}
MT_COSMOS_KEY: ${{ secrets.AZURE_COSMOSKEY }}
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter "Category!=Flaky&Category!=Integration"
+ run: dotnet test -c Release --logger GitHubActions --filter "Category!=Flaky&Category!=Integration"
working-directory: tests/MassTransit.Azure.Cosmos.Tests
test-dapper:
name: "Storage: Dapper"
@@ -238,7 +248,7 @@ jobs:
runs-on: ubuntu-latest
services:
mssql:
- image: mcr.microsoft.com/mssql/server:2017-latest
+ image: mcr.microsoft.com/azure-sql-edge
env:
ACCEPT_EULA: Y
SA_PASSWORD: "Password12!"
@@ -249,15 +259,15 @@ jobs:
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: false
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test Dapper
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter "Category!=Flaky&Category!=Integration"
+ run: dotnet test -c Release --logger GitHubActions --filter "Category!=Flaky&Category!=Integration"
working-directory: tests/MassTransit.DapperIntegration.Tests
test-entity-framework:
name: "Storage: EntityFramework"
@@ -265,7 +275,7 @@ jobs:
runs-on: ubuntu-latest
services:
mssql:
- image: mcr.microsoft.com/mssql/server:2017-latest
+ image: mcr.microsoft.com/azure-sql-edge
env:
ACCEPT_EULA: Y
SA_PASSWORD: "Password12!"
@@ -283,19 +293,19 @@ jobs:
DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: false
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- - name: Test EntityFrameworkCore 6.0
- run: dotnet test -c Release --logger:"console;verbosity=normal" -f net6.0 --filter "Category!=Flaky&Category!=Integration"
+ - name: Test EntityFrameworkCore
+ run: dotnet test -c Release --logger GitHubActions --filter "Category!=Flaky&Category!=Integration"
working-directory: tests/MassTransit.EntityFrameworkCoreIntegration.Tests
- name: Test EntityFramework
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter "Category!=Flaky&Category!=Integration"
+ run: dotnet test -c Release --logger GitHubActions --filter "Category!=Flaky&Category!=Integration"
working-directory: tests/MassTransit.EntityFrameworkIntegration.Tests
test-marten:
name: "Storage: Marten"
@@ -311,36 +321,41 @@ jobs:
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test Marten
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.MartenIntegration.Tests
test-mongo:
name: "Storage: MongoDB"
timeout-minutes: 10
runs-on: ubuntu-latest
- services:
- mongo:
- image: mongo
- ports:
- - '27017-27019:27017-27019'
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
+
+ - name: Spin up test environment
+ run: |
+ docker compose -f docker-compose.yml up -d
+ working-directory: tests/MassTransit.MongoDbIntegration.Tests
- name: Test MongoDB
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
+ working-directory: tests/MassTransit.MongoDbIntegration.Tests
+
+ - name: Stop test environment
+ run: |
+ docker compose -f docker-compose.yml down
working-directory: tests/MassTransit.MongoDbIntegration.Tests
test-nhibernate:
name: "Storage: NHibernate"
@@ -348,15 +363,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test NHibernate
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.NHibernateIntegration.Tests
test-redis:
name: "Storage: Redis"
@@ -370,15 +385,37 @@ jobs:
options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test Redis
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
+ working-directory: tests/MassTransit.RedisIntegration.Tests
+ test-valkey:
+ name: "Storage: Valkey"
+ timeout-minutes: 10
+ runs-on: ubuntu-latest
+ services:
+ valkey:
+ image: valkey/valkey:8
+ ports:
+ - '6379:6379'
+ options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
+ steps:
+ - name: Check out code
+ uses: actions/checkout@v4
+
+ - name: Install .NET Core SDK
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: Test Valkey
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.RedisIntegration.Tests
test-hangfire:
name: "Scheduler: Hangfire"
@@ -386,15 +423,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test Hangfire
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.HangfireIntegration.Tests
test-quartz:
name: "Scheduler: Quartz"
@@ -402,91 +439,68 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test Quartz
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.QuartzIntegration.Tests
test-eventhub:
name: "Rider: EventHub"
runs-on: ubuntu-latest
- if: false # turned off for flakey
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
+
+ - name: Spin up test environment
+ run: |
+ docker compose -f docker-compose.yml up -d
+ working-directory: tests/MassTransit.EventHubIntegration.Tests
- name: Test EventHub
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
env:
MT_EH_NAMESPACE: ${{ secrets.AZURE_EVENTHUB }}
MT_AZURE_STORAGE_ACCOUNT: ${{ secrets.AZURE_STORAGE }}
working-directory: tests/MassTransit.EventHubIntegration.Tests
+
+ - name: Stop test environment
+ run: |
+ docker compose -f docker-compose.yml down
+ working-directory: tests/MassTransit.EventHubIntegration.Tests
test-kafka:
name: "Rider: Kafka"
runs-on: ubuntu-latest
- if: true # turned off for flakey
- services:
- zookeeper:
- image: confluentinc/cp-zookeeper:latest
- ports:
- - "2181:2181"
- env:
- ZOOKEEPER_CLIENT_PORT: 2181
- ZOOKEEPER_TICK_TIME: 2000
- broker:
- image: confluentinc/cp-kafka:latest
- ports:
- - "29092:29092"
- - "9092:9092"
- - "9101:9101"
- env:
- KAFKA_BROKER_ID: 1
- KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
- KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
- KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://broker:29092,PLAINTEXT_HOST://localhost:9092
- KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
- KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
- KAFKA_CONFLUENT_LICENSE_TOPIC_REPLICATION_FACTOR: 1
- KAFKA_CONFLUENT_BALANCER_TOPIC_REPLICATION_FACTOR: 1
- KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
- KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
- KAFKA_JMX_PORT: 9101
- KAFKA_JMX_HOSTNAME: localhost
- KAFKA_CONFLUENT_SCHEMA_REGISTRY_URL: http://schema-registry:8081
- CONFLUENT_METRICS_REPORTER_BOOTSTRAP_SERVERS: broker:29092
- CONFLUENT_METRICS_REPORTER_TOPIC_REPLICAS: 1
- CONFLUENT_METRICS_ENABLE: 'false'
- options: --health-cmd "nc -vz localhost 9092 || exit 1" --health-interval 10s --health-timeout 5s --health-retries 5
- schema-registry:
- image: confluentinc/cp-schema-registry:latest
- ports:
- - "8081:8081"
- env:
- SCHEMA_REGISTRY_HOST_NAME: schema-registry
- SCHEMA_REGISTRY_KAFKASTORE_BOOTSTRAP_SERVERS: 'broker:29092'
- SCHEMA_REGISTRY_LISTENERS: http://0.0.0.0:8081
- options: --health-cmd "curl --fail http://localhost:8081/subjects || exit 1" --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
+
+ - name: Spin up test environment
+ run: |
+ docker compose -f docker-compose.yml up -d
+ working-directory: tests/MassTransit.KafkaIntegration.Tests
- name: Test Kafka
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
+ working-directory: tests/MassTransit.KafkaIntegration.Tests
+
+ - name: Stop test environment
+ run: |
+ docker compose -f docker-compose.yml down
working-directory: tests/MassTransit.KafkaIntegration.Tests
test-signalr:
@@ -495,15 +509,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Test SignalR
- run: dotnet test -c Release --logger:"console;verbosity=normal" --filter Category!=Flaky
+ run: dotnet test -c Release --logger GitHubActions --filter Category!=Flaky
working-directory: tests/MassTransit.SignalR.Tests
calc-version:
@@ -512,12 +526,11 @@ jobs:
needs:
- compile
- test-ubuntu
- - test-containers
- test-activemq
# - test-azure-service-bus
- - test-grpc
- test-rabbitmq
- test-sqs
+ - test-sql-transport
- test-azure-table
- test-cosmosdb
- test-dapper
@@ -526,6 +539,7 @@ jobs:
- test-mongo
- test-nhibernate
- test-redis
+ - test-valkey
- test-hangfire
- test-quartz
# - test-eventhub
@@ -555,12 +569,12 @@ jobs:
echo "${{ needs.calc-version.outputs.version }}"
- name: Check out code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install .NET Core SDK
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@v4
with:
- dotnet-version: '6.0.x'
+ dotnet-version: '8.0.x'
- name: Build and Publish MassTransit
# was: brandedoutcast/publish-nuget@v2.5.5
@@ -586,25 +600,26 @@ jobs:
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
- - name: Build and Publish MassTransit.Analyzers
+ - name: Build and Publish MassTransit MessagePack
+ # was: brandedoutcast/publish-nuget@v2.5.5
uses: drusellers/publish-nuget@master
with:
- project-file-path: src/MassTransit.Analyzers/MassTransit.Analyzers.csproj
+ project-file-path: src/MassTransit.MessagePack/MassTransit.MessagePack.csproj
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
- include-symbols: false
- - name: Build and Publish MassTransit.Interop.NServiceBus
+ - name: Build and Publish MassTransit.Analyzers
uses: drusellers/publish-nuget@master
with:
- project-file-path: src/MassTransit.Interop.NServiceBus/MassTransit.Interop.NServiceBus.csproj
+ project-file-path: src/MassTransit.Analyzers/MassTransit.Analyzers.csproj
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
- - name: Build and Publish MassTransit.PrometheusIntegration
+ include-symbols: false
+ - name: Build and Publish MassTransit.Interop.NServiceBus
uses: drusellers/publish-nuget@master
with:
- project-file-path: src/MassTransit.PrometheusIntegration/MassTransit.PrometheusIntegration.csproj
+ project-file-path: src/MassTransit.Interop.NServiceBus/MassTransit.Interop.NServiceBus.csproj
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
@@ -657,6 +672,13 @@ jobs:
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
+ - name: Build and Publish MassTransit.AmazonS3
+ uses: drusellers/publish-nuget@master
+ with:
+ project-file-path: src/Persistence/MassTransit.AmazonS3/MassTransit.AmazonS3.csproj
+ version: ${{ needs.calc-version.outputs.version }}
+ tag-commit: false
+ nuget-key: ${{secrets.NUGET_API_KEY}}
- name: Build and Publish MassTransit.EntityFrameworkCoreIntegration
uses: drusellers/publish-nuget@master
with:
@@ -734,17 +756,24 @@ jobs:
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
- - name: Build and Publish MassTransit.EventHubIntegration
+ - name: Build and Publish MassTransit.SqlTransport.PostreSql
uses: drusellers/publish-nuget@master
with:
- project-file-path: src/Transports/MassTransit.EventHubIntegration/MassTransit.EventHubIntegration.csproj
+ project-file-path: src/Transports/MassTransit.SqlTransport.PostgreSql/MassTransit.SqlTransport.PostgreSql.csproj
+ version: ${{ needs.calc-version.outputs.version }}
+ tag-commit: false
+ nuget-key: ${{secrets.NUGET_API_KEY}}
+ - name: Build and Publish MassTransit.SqlTransport.SqlServer
+ uses: drusellers/publish-nuget@master
+ with:
+ project-file-path: src/Transports/MassTransit.SqlTransport.SqlServer/MassTransit.SqlTransport.SqlServer.csproj
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
- - name: Build and Publish MassTransit.Grpc
+ - name: Build and Publish MassTransit.EventHubIntegration
uses: drusellers/publish-nuget@master
with:
- project-file-path: src/Transports/MassTransit.GrpcTransport/MassTransit.GrpcTransport.csproj
+ project-file-path: src/Transports/MassTransit.EventHubIntegration/MassTransit.EventHubIntegration.csproj
version: ${{ needs.calc-version.outputs.version }}
tag-commit: false
nuget-key: ${{secrets.NUGET_API_KEY}}
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
deleted file mode 100644
index 33e8d01d4cd..00000000000
--- a/.github/workflows/docs.yml
+++ /dev/null
@@ -1,38 +0,0 @@
-name: MassTransit Documentation
-on:
- push:
- branches:
- - develop
- paths:
- - 'docs/**'
- - 'package.json'
- - '**/docs.yml'
-jobs:
- build:
- name: Build and Deploy
- runs-on: ubuntu-latest
- if: (github.ref == 'refs/heads/develop') && github.repository == 'MassTransit/MassTransit'
- steps:
- - name: Check out code
- uses: actions/checkout@v2
-
- - name: Build
- run: |
- npm install
- npm run docs:build
-
- - name: Deploy
- working-directory: ./docs/.vuepress/dist
- run: |
- git init .
- git config --global user.name "MassTransit"
- git config --global user.email "mtproj@phatboyg.com"
- git fetch https://github.com/MassTransit/masstransit.github.io.git
- git checkout 220443cd2ab45d486fcee10a65669aff0bda31ab
- git checkout -b master
- git add .
- git commit -am "Deploy Documentation"
- git push --force --set-upstream https://${{secrets.PHATBOYG_PAT}}:x-oauth-basic@github.com/MassTransit/masstransit.github.io.git master
-
-
-
diff --git a/.github/workflows/nightly-transports.yml b/.github/workflows/nightly-transports.yml
index 0a712b8d4b6..53d2297fb8e 100644
--- a/.github/workflows/nightly-transports.yml
+++ b/.github/workflows/nightly-transports.yml
@@ -42,15 +42,15 @@ jobs:
DOCKER_HOST: "unix:///var/run/docker.sock"
steps:
- name: Check out code
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Install .NET 3.1
- uses: actions/setup-dotnet@v1
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: '3.1.x'
- name: Install .NET 5
- uses: actions/setup-dotnet@v1
+ uses: actions/setup-dotnet@v4
with:
dotnet-version: '5.0.x'
diff --git a/COPYRIGHT b/COPYRIGHT
index 03e1cb9ac15..c86b7364da8 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -1,4 +1,4 @@
-Copyright 2007-2019 Chris Patterson
+Copyright 2007-2024 Chris Patterson
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
diff --git a/Directory.Build.props b/Directory.Build.props
index 75680cb2973..3ade404e6b2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -7,9 +7,9 @@
- 8
+ 12
4
- CS1587,CS1591,CS1998,NU5105
+ CS1591,CS1998
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6d55fcd8576..951564e7c96 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,87 +3,109 @@
true
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MassTransit.sln b/MassTransit.sln
index e81100fb674..b54f864917b 100644
--- a/MassTransit.sln
+++ b/MassTransit.sln
@@ -21,8 +21,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.RabbitMqTranspo
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.RabbitMqTransport.Tests", "tests\MassTransit.RabbitMqTransport.Tests\MassTransit.RabbitMqTransport.Tests.csproj", "{E4C3F51F-B7CE-4521-80BC-E91A9B0F6FFD}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.Containers.Tests", "tests\MassTransit.Containers.Tests\MassTransit.Containers.Tests.csproj", "{9CE51963-9E51-48EE-A75F-62C9CB0CF23C}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".solution", ".solution", "{2C8A15FA-A445-4916-AAC2-3BE53AA247A7}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
@@ -89,8 +87,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.SignalR.Tests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.WebJobs.EventHubsIntegration", "src\Transports\MassTransit.WebJobs.EventHubsIntegration\MassTransit.WebJobs.EventHubsIntegration.csproj", "{56E9DB90-C063-49CA-BAF7-FFCFE963E44C}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Monitoring", "Monitoring", "{DE40BDEC-6277-4E14-8EB6-D0B6863571BC}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.Azure.Storage", "src\Persistence\MassTransit.Azure.Storage\MassTransit.Azure.Storage.csproj", "{C81070F9-908B-4A76-A96F-CFD4F67A93AB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analyzers", "Analyzers", "{393B7B0C-52F4-41E8-A9E9-EE867A0E6448}"
@@ -103,10 +99,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.HangfireIntegra
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.HangfireIntegration.Tests", "tests\MassTransit.HangfireIntegration.Tests\MassTransit.HangfireIntegration.Tests.csproj", "{D4ED7C0B-DADE-45FA-BCCB-9E5D6C27D714}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.PrometheusIntegration", "src\MassTransit.PrometheusIntegration\MassTransit.PrometheusIntegration.csproj", "{1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.PrometheusIntegration.Tests", "tests\MassTransit.PrometheusIntegration.Tests\MassTransit.PrometheusIntegration.Tests.csproj", "{A29B1D94-6F11-487E-B4A1-3460928EC2C0}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interoperability", "Interoperability", "{4F40E08B-7C24-4D2A-8476-B7F93D0A2910}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MassTransit.Interop.NServiceBus", "src\MassTransit.Interop.NServiceBus\MassTransit.Interop.NServiceBus.csproj", "{F7F32EF2-0586-4FD8-90AD-AD7C792DF5E7}"
@@ -125,30 +117,28 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.Azure.Table", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.Azure.Table.Tests", "tests\MassTransit.Azure.Table.Tests\MassTransit.Azure.Table.Tests.csproj", "{17E04A9B-5DB5-4857-99CD-E9AF22DA00E4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.GrpcTransport", "src\Transports\MassTransit.GrpcTransport\MassTransit.GrpcTransport.csproj", "{AF56509B-0B95-4ACE-9CFC-F9A79756C357}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.GrpcTransport.Tests", "tests\MassTransit.GrpcTransport.Tests\MassTransit.GrpcTransport.Tests.csproj", "{082488F6-6322-462A-85D7-9E893A67B8CD}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.Transports.Tests", "tests\MassTransit.Transports.Tests\MassTransit.Transports.Tests.csproj", "{7632EDAF-29A1-4E69-952F-C75D3BF34B3B}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.Abstractions", "src\MassTransit.Abstractions\MassTransit.Abstractions.csproj", "{2CAF0C51-2F64-4EB0-8E27-AE3E0085CA75}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.StateMachineVisualizer", "src\MassTransit.StateMachineVisualizer\MassTransit.StateMachineVisualizer.csproj", "{8CD4298E-962B-4D04-821D-274343099E83}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.Abstractions.Tests", "tests\MassTransit.Abstractions.Tests\MassTransit.Abstractions.Tests.csproj", "{AB188378-1BAC-4ECB-98A7-91E12C861381}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.BenchmarkConsole", "tests\MassTransit.BenchmarkConsole\MassTransit.BenchmarkConsole.csproj", "{2B6ACCF2-0CAF-4152-901F-0048390971E4}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.Benchmark", "tests\MassTransit.Benchmark\MassTransit.Benchmark.csproj", "{667E52D5-E1D9-49EE-B364-3CB7E43EE160}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.Newtonsoft", "src\MassTransit.Newtonsoft\MassTransit.Newtonsoft.csproj", "{388085C8-1BC8-48F8-8EA2-3698FCB385E5}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarking", "Benchmarking", "{BF384860-70ED-47F0-B276-13D2DA9ECD87}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.DynamoDbIntegration", "src\Persistence\MassTransit.DynamoDbIntegration\MassTransit.DynamoDbIntegration.csproj", "{186D6491-ECCD-49EB-8E99-AFD7AD6037D2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.DynamoDbIntegration.Tests", "tests\MassTransit.DynamoDbIntegration.Tests\MassTransit.DynamoDbIntegration.Tests.csproj", "{694E06CF-2842-4E71-8CD2-81FA7C1B3D13}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.AmazonS3", "src\Persistence\MassTransit.AmazonS3\MassTransit.AmazonS3.csproj", "{86905425-64C4-4EB0-8884-F2BD27782310}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.SqlTransport.PostgreSql", "src\Transports\MassTransit.SqlTransport.PostgreSql\MassTransit.SqlTransport.PostgreSql.csproj", "{AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.SqlTransport.SqlServer", "src\Transports\MassTransit.SqlTransport.SqlServer\MassTransit.SqlTransport.SqlServer.csproj", "{757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.SqlTransport.Tests", "tests\MassTransit.SqlTransport.Tests\MassTransit.SqlTransport.Tests.csproj", "{814826C2-6F89-4BF2-BDA9-D6116F2F98A2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MassTransit.MessagePack", "src\MassTransit.MessagePack\MassTransit.MessagePack.csproj", "{EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -216,14 +206,6 @@ Global
{E4C3F51F-B7CE-4521-80BC-E91A9B0F6FFD}.Release|x86.ActiveCfg = Release|Any CPU
{E4C3F51F-B7CE-4521-80BC-E91A9B0F6FFD}.ReleaseUnsigned|Any CPU.ActiveCfg = Release|Any CPU
{E4C3F51F-B7CE-4521-80BC-E91A9B0F6FFD}.ReleaseUnsigned|x86.ActiveCfg = Release|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.Debug|x86.ActiveCfg = Debug|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.Release|Any CPU.Build.0 = Release|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.Release|x86.ActiveCfg = Release|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.ReleaseUnsigned|Any CPU.ActiveCfg = Release|Any CPU
- {9CE51963-9E51-48EE-A75F-62C9CB0CF23C}.ReleaseUnsigned|x86.ActiveCfg = Release|Any CPU
{2F7DB49B-B650-4B57-BB9D-5D1B913CCACC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F7DB49B-B650-4B57-BB9D-5D1B913CCACC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F7DB49B-B650-4B57-BB9D-5D1B913CCACC}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -584,30 +566,6 @@ Global
{D4ED7C0B-DADE-45FA-BCCB-9E5D6C27D714}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
{D4ED7C0B-DADE-45FA-BCCB-9E5D6C27D714}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
{D4ED7C0B-DADE-45FA-BCCB-9E5D6C27D714}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Debug|x86.ActiveCfg = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Debug|x86.Build.0 = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Release|Any CPU.Build.0 = Release|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Release|x86.ActiveCfg = Release|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.Release|x86.Build.0 = Release|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Debug|x86.Build.0 = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Release|Any CPU.Build.0 = Release|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Release|x86.ActiveCfg = Release|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.Release|x86.Build.0 = Release|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
{F7F32EF2-0586-4FD8-90AD-AD7C792DF5E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7F32EF2-0586-4FD8-90AD-AD7C792DF5E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7F32EF2-0586-4FD8-90AD-AD7C792DF5E7}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -692,42 +650,6 @@ Global
{17E04A9B-5DB5-4857-99CD-E9AF22DA00E4}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
{17E04A9B-5DB5-4857-99CD-E9AF22DA00E4}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
{17E04A9B-5DB5-4857-99CD-E9AF22DA00E4}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Debug|x86.ActiveCfg = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Debug|x86.Build.0 = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Release|Any CPU.Build.0 = Release|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Release|x86.ActiveCfg = Release|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.Release|x86.Build.0 = Release|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Debug|x86.ActiveCfg = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Debug|x86.Build.0 = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Release|Any CPU.Build.0 = Release|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Release|x86.ActiveCfg = Release|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.Release|x86.Build.0 = Release|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
- {082488F6-6322-462A-85D7-9E893A67B8CD}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Debug|x86.ActiveCfg = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Debug|x86.Build.0 = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Release|Any CPU.Build.0 = Release|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Release|x86.ActiveCfg = Release|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.Release|x86.Build.0 = Release|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
{2CAF0C51-2F64-4EB0-8E27-AE3E0085CA75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2CAF0C51-2F64-4EB0-8E27-AE3E0085CA75}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2CAF0C51-2F64-4EB0-8E27-AE3E0085CA75}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -764,30 +686,6 @@ Global
{AB188378-1BAC-4ECB-98A7-91E12C861381}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
{AB188378-1BAC-4ECB-98A7-91E12C861381}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
{AB188378-1BAC-4ECB-98A7-91E12C861381}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Debug|x86.Build.0 = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Release|Any CPU.Build.0 = Release|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Release|x86.ActiveCfg = Release|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.Release|x86.Build.0 = Release|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
- {2B6ACCF2-0CAF-4152-901F-0048390971E4}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Debug|x86.ActiveCfg = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Debug|x86.Build.0 = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Release|Any CPU.Build.0 = Release|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Release|x86.ActiveCfg = Release|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.Release|x86.Build.0 = Release|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
{388085C8-1BC8-48F8-8EA2-3698FCB385E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{388085C8-1BC8-48F8-8EA2-3698FCB385E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{388085C8-1BC8-48F8-8EA2-3698FCB385E5}.Debug|x86.ActiveCfg = Debug|Any CPU
@@ -824,6 +722,66 @@ Global
{694E06CF-2842-4E71-8CD2-81FA7C1B3D13}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
{694E06CF-2842-4E71-8CD2-81FA7C1B3D13}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
{694E06CF-2842-4E71-8CD2-81FA7C1B3D13}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Debug|x86.Build.0 = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Release|x86.ActiveCfg = Release|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.Release|x86.Build.0 = Release|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
+ {86905425-64C4-4EB0-8884-F2BD27782310}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Debug|x86.Build.0 = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Release|x86.ActiveCfg = Release|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.Release|x86.Build.0 = Release|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Debug|x86.Build.0 = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Release|x86.ActiveCfg = Release|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.Release|x86.Build.0 = Release|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Debug|x86.Build.0 = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Release|x86.ActiveCfg = Release|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.Release|x86.Build.0 = Release|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Debug|x86.Build.0 = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Release|x86.ActiveCfg = Release|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.Release|x86.Build.0 = Release|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.ReleaseUnsigned|Any CPU.ActiveCfg = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.ReleaseUnsigned|Any CPU.Build.0 = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.ReleaseUnsigned|x86.ActiveCfg = Debug|Any CPU
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD}.ReleaseUnsigned|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -864,8 +822,6 @@ Global
{1F71C6D2-17AE-4C7B-878D-BB7B295D3B3D} = {393B7B0C-52F4-41E8-A9E9-EE867A0E6448}
{CF2D2B2E-2C34-4E8C-970F-4CB07DB0457B} = {FDC1A760-D4E5-44F4-B0D9-C19F2E14253A}
{D4ED7C0B-DADE-45FA-BCCB-9E5D6C27D714} = {FDC1A760-D4E5-44F4-B0D9-C19F2E14253A}
- {1AF687AB-41DE-4B2A-97F5-E9BBF5E51C75} = {DE40BDEC-6277-4E14-8EB6-D0B6863571BC}
- {A29B1D94-6F11-487E-B4A1-3460928EC2C0} = {DE40BDEC-6277-4E14-8EB6-D0B6863571BC}
{F7F32EF2-0586-4FD8-90AD-AD7C792DF5E7} = {4F40E08B-7C24-4D2A-8476-B7F93D0A2910}
{1FC221F1-E49D-473C-9869-4452B2C4EBEE} = {F4CD08DB-40C0-4476-A135-DB8E0BCB79F8}
{555EC658-5EF6-4C10-ABA0-B717896398BE} = {F4CD08DB-40C0-4476-A135-DB8E0BCB79F8}
@@ -874,14 +830,14 @@ Global
{24CC1B47-AE01-4871-A939-2BF6EB789860} = {F4CD08DB-40C0-4476-A135-DB8E0BCB79F8}
{338E2B7E-4301-4547-9172-4415F739C029} = {56F516D7-BC3C-49E1-A639-83C5F14953F8}
{17E04A9B-5DB5-4857-99CD-E9AF22DA00E4} = {56F516D7-BC3C-49E1-A639-83C5F14953F8}
- {AF56509B-0B95-4ACE-9CFC-F9A79756C357} = {0006D6BB-1382-4B32-AD32-CA037F5CD4F6}
- {082488F6-6322-462A-85D7-9E893A67B8CD} = {0006D6BB-1382-4B32-AD32-CA037F5CD4F6}
- {7632EDAF-29A1-4E69-952F-C75D3BF34B3B} = {0006D6BB-1382-4B32-AD32-CA037F5CD4F6}
{388085C8-1BC8-48F8-8EA2-3698FCB385E5} = {4F40E08B-7C24-4D2A-8476-B7F93D0A2910}
- {667E52D5-E1D9-49EE-B364-3CB7E43EE160} = {BF384860-70ED-47F0-B276-13D2DA9ECD87}
- {2B6ACCF2-0CAF-4152-901F-0048390971E4} = {BF384860-70ED-47F0-B276-13D2DA9ECD87}
{186D6491-ECCD-49EB-8E99-AFD7AD6037D2} = {56F516D7-BC3C-49E1-A639-83C5F14953F8}
{694E06CF-2842-4E71-8CD2-81FA7C1B3D13} = {56F516D7-BC3C-49E1-A639-83C5F14953F8}
+ {86905425-64C4-4EB0-8884-F2BD27782310} = {56F516D7-BC3C-49E1-A639-83C5F14953F8}
+ {AB7996F8-8877-4FE6-AFC1-58A6DF41FAB4} = {0006D6BB-1382-4B32-AD32-CA037F5CD4F6}
+ {757A9FD1-099A-4CF5-BCF9-7615FCD78CF1} = {0006D6BB-1382-4B32-AD32-CA037F5CD4F6}
+ {814826C2-6F89-4BF2-BDA9-D6116F2F98A2} = {0006D6BB-1382-4B32-AD32-CA037F5CD4F6}
+ {EF197CD3-EE31-44F5-95C4-76DCAC3E10DD} = {4F40E08B-7C24-4D2A-8476-B7F93D0A2910}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {43D3A7D5-0945-435E-8D03-1E631E5CDBA8}
diff --git a/MassTransit.sln.DotSettings b/MassTransit.sln.DotSettings
index f30e4ae292b..00bee3f9ac2 100644
--- a/MassTransit.sln.DotSettings
+++ b/MassTransit.sln.DotSettings
@@ -5,6 +5,7 @@
DO_NOT_SHOW
WARNING
DO_NOT_SHOW
+ DO_NOT_SHOW
OFF
<?xml version="1.0" encoding="utf-16"?><Profile name="Normal"><HtmlReformatCode>True</HtmlReformatCode><JsReformatCode>True</JsReformatCode><XMLReformatCode>True</XMLReformatCode><CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" /><CssReformatCode>True</CssReformatCode><VBReformatCode>True</VBReformatCode><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><CSUpdateFileHeader>True</CSUpdateFileHeader><CSEnforceVarKeywordUsageSettings>True</CSEnforceVarKeywordUsageSettings><RemoveCodeRedundancies>True</RemoveCodeRedundancies><CSArrangeQualifiers>True</CSArrangeQualifiers><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSShortenReferences>True</CSShortenReferences><IDEA_SETTINGS><profile version="1.0">
<option name="myName" value="Normal" />
@@ -35,8 +36,9 @@
True
True
OUTDENT
- OUTDENT
+ USUAL_INDENT
OUTDENT
+ INDENT
1
False
False
@@ -214,6 +216,10 @@
<Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy>
<Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb_aaBb" /><ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy>
+ <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy></Policy>
+ <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /></Policy>
+ <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Types and namespaces"><ElementKinds><Kind Name="NAMESPACE" /><Kind Name="CLASS" /><Kind Name="STRUCT" /><Kind Name="ENUM" /><Kind Name="DELEGATE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb_aaBb" /><ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /></Policy></Policy>
+ <Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Interfaces"><ElementKinds><Kind Name="INTERFACE" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy></Policy>
BOTH_SIDES
OUTLINE
@@ -241,6 +247,7 @@
True
True
True
+ True
True
True
@@ -277,6 +284,7 @@ public async System.Threading.Tasks.Task $TESTNAME$()
True
True
True
+ True
True
True
True
diff --git a/NOTICE b/NOTICE
index 32386aa35b0..6b3752b3b66 100644
--- a/NOTICE
+++ b/NOTICE
@@ -4,4 +4,4 @@
=========================================================================
MassTransit
-Copyright 2007-2019 Chris Patterson
+Copyright 2007-2024 Chris Patterson
diff --git a/NuGet.README.md b/NuGet.README.md
index b3fc1d63ae4..09dbb29f882 100644
--- a/NuGet.README.md
+++ b/NuGet.README.md
@@ -2,10 +2,10 @@
MassTransit provides a developer-focused, modern platform for creating distributed applications without complexity.
- - First class testing support
- - Write once, then deploy using RabbitMQ, Azure Service Bus, and Amazon SQS
- - Observability via Open Telemetry (OTEL)
- - Fully-supported, widely-adopted, a complete end-to-end solution
+- First class testing support
+- Write once, then deploy using RabbitMQ, Azure Service Bus, and Amazon SQS
+- Observability via Open Telemetry (OTEL)
+- Fully-supported, widely-adopted, a complete end-to-end solution
## Documentation
@@ -41,6 +41,7 @@ The following NuGet packages are the currently supported.
### Saga Persistence
+* [MassTransit.AmazonS3](https://nuget.org/packages/MassTransit.AmazonS3/)
* [MassTransit.Azure.Cosmos](https://nuget.org/packages/MassTransit.Azure.Cosmos/)
* [MassTransit.Azure.Cosmos.Table](https://nuget.org/packages/MassTransit.Azure.Cosmos.Table/)
* [MassTransit.DapperIntegration](https://nuget.org/packages/MassTransit.DapperIntegration/)
@@ -111,7 +112,7 @@ The following packages from earlier versions of MassTransit are no longer suppor
* MassTransit.StructureMapSigned
* MassTransit.Unity
-## Discord
+## Discord
Get help live at the MassTransit Discord server.
@@ -119,6 +120,7 @@ Get help live at the MassTransit Discord server.
## GitHub Issues
-> Please do not open an issue on GitHub, unless you have spotted an actual bug in MassTransit.
+> Please do not open an issue on GitHub, unless you have spotted an actual bug in MassTransit.
-Use [GitHub Discussions](https://github.com/MassTransit/MassTransit/discussions) to ask questions, bring up ideas, or other general items. Issues are not the place for questions, and will either be converted to a discussion or closed.
+Use [GitHub Discussions](https://github.com/MassTransit/MassTransit/discussions) to ask questions, bring up ideas, or other general items. Issues are not the
+place for questions, and will either be converted to a discussion or closed.
diff --git a/README.md b/README.md
index e1925597284..5249fda910e 100644
--- a/README.md
+++ b/README.md
@@ -18,51 +18,53 @@ Build Status
|---------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| master | [![master](https://github.com/MassTransit/MassTransit/actions/workflows/build.yml/badge.svg?branch=master&event=push)](https://github.com/MassTransit/MassTransit/actions/workflows/build.yml) |
| develop | [![develop](https://github.com/MassTransit/MassTransit/actions/workflows/build.yml/badge.svg?branch=develop&event=push)](https://github.com/MassTransit/MassTransit/actions/workflows/build.yml) |
-| documentation | [![documentation](https://github.com/MassTransit/MassTransit/actions/workflows/docs.yml/badge.svg?branch=develop&event=push)](https://github.com/MassTransit/MassTransit/actions/workflows/docs.yml) |
MassTransit NuGet Packages
---------------------------
-| Package Name | .NET | .NET Standard | .NET Framework |
-|-----------------------------------------------------------------|:----:|:-------------:|:--------------:|
-| **Main** | | | |
-| [MassTransit][MassTransit.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Abstractions][MassTransitAbstractions.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Newtonsoft][MassTransitNewtonsoft.nuget] | 6.0 | 2.0 | 4.6.1 |
-| **Other** | | | |
-| [MassTransit.Analyzers][Analyzers.nuget] | | 2.0 | |
-| [MassTransit.Templates][Templates.nuget] | 5.0 | | |
-| [MassTransit.SignalR][SignalR.nuget] | 6.0 | | 4.6.1 |
-| [MassTransit.Interop.NServiceBus][MassTransitNServiceBus.nuget] | 6.0 | 2.0 | 4.6.1 |
-| [MassTransit.TestFramework][TestFramework.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| **Monitoring** | | | |
-| [MassTransit.Prometheus][Prometheus.nuget] | 6.0 | 2.0 | 4.6.2 |
-| **Persistence** | | | |
-| [MassTransit.Azure.Cosmos][Cosmos.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Azure.Storage][AzureStorage.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Azure.Table][AzureTable.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Dapper][Dapper.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.DynamoDb][DynamoDb.nuget] | 6.0 | 2.0 | 4.6.1 |
-| [MassTransit.EntityFrameworkCore][EFCore.nuget] | 6.0 | 2.0 | |
-| [MassTransit.EntityFramework][EF.nuget] | | 2.1 | 4.6.1 |
-| [MassTransit.Marten][Marten.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.MongoDb][MongoDb.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.NHibernate][NHibernate.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Redis][Redis.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| **Scheduling** | | | |
-| [MassTransit.Hangfire][Hangfire.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Quartz][Quartz.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| **Transports** | | | |
-| [MassTransit.ActiveMQ][ActiveMQ.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.AmazonSQS][AmazonSQS.nuget] | 6.0 | 2.0 | 4.6.1 |
-| [MassTransit.Azure.ServiceBus.Core][AzureSbCore.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.Grpc][Grpc.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.RabbitMQ][RabbitMQ.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.WebJobs.EventHubs][EventHubs.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.WebJobs.ServiceBus][AzureFunc.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| **Riders** | | | |
-| [MassTransit.Kafka][Kafka.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
-| [MassTransit.EventHub][EventHub.nuget] | 6.0 | 2.0, 2.1 | 4.6.1 |
+| Package Name | .NET | .NET Standard | .NET Framework |
+|-----------------------------------------------------------------|:--------:|:-------------:|:--------------:|
+| **Main** | | | |
+| [MassTransit][MassTransit.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Abstractions][MassTransitAbstractions.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Newtonsoft][MassTransitNewtonsoft.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.MessagePack][MassTransitMessagePack.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| **Other** | | | |
+| [MassTransit.Analyzers][Analyzers.nuget] | | 2.0 | |
+| [MassTransit.Templates][Templates.nuget] | 6.0 | | |
+| [MassTransit.SignalR][SignalR.nuget] | 6.0, 8.0 | | 4.7.2 |
+| [MassTransit.Interop.NServiceBus][MassTransitNServiceBus.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.TestFramework][TestFramework.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| **Monitoring** | | | |
+| [MassTransit.Prometheus][Prometheus.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| **Persistence** | | | |
+| [MassTransit.AmazonS3][AmazonS3.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Azure.Cosmos][Cosmos.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Azure.Storage][AzureStorage.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Azure.Table][AzureTable.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Dapper][Dapper.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.DynamoDb][DynamoDb.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.EntityFrameworkCore][EFCore.nuget] | 6.0, 8.0 | 2.0 | |
+| [MassTransit.EntityFramework][EF.nuget] | | 2.1 | 4.7.2 |
+| [MassTransit.Marten][Marten.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.MongoDb][MongoDb.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.NHibernate][NHibernate.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Redis][Redis.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| **Scheduling** | | | |
+| [MassTransit.Hangfire][Hangfire.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Quartz][Quartz.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| **Transports** | | | |
+| [MassTransit.ActiveMQ][ActiveMQ.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.AmazonSQS][AmazonSQS.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.Azure.ServiceBus.Core][AzureSbCore.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.RabbitMQ][RabbitMQ.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.SqlTransport.PostgreSQL][PostgreSQL.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.SqlTransport.SqlServer][SqlServer.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.WebJobs.EventHubs][EventHubs.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.WebJobs.ServiceBus][AzureFunc.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| **Riders** | | | |
+| [MassTransit.Kafka][Kafka.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
+| [MassTransit.EventHub][EventHub.nuget] | 6.0, 8.0 | 2.0 | 4.7.2 |
## Discord
@@ -83,7 +85,7 @@ enhancements and calls for help from people who forget to check back if they get
## Building from Source
- 1. Install the latest [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
+ 1. Install the latest [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
2. Clone the source down to your machine
```bash
git clone https://github.com/MassTransit/MassTransit.git
@@ -100,7 +102,7 @@ enhancements and calls for help from people who forget to check back if they get
3. Make a pull request
# REQUIREMENTS
-* .NET 6 SDK
+* .NET 8 SDK
# CREDITS
Logo Design by _The Agile Badger_
@@ -108,6 +110,7 @@ Logo Design by _The Agile Badger_
[MassTransit.nuget]: https://www.nuget.org/packages/MassTransit
[MassTransitAbstractions.nuget]: https://www.nuget.org/packages/MassTransit.Abstractions
[MassTransitNewtonsoft.nuget]: https://www.nuget.org/packages/MassTransit.Newtonsoft
+[MassTransitMessagePack.nuget]: https://www.nuget.org/packages/MassTransit.MessagePack
[MassTransitNServiceBus.nuget]: https://www.nuget.org/packages/MassTransit.Interop.NServiceBus
[Analyzers.nuget]: https://www.nuget.org/packages/MassTransit.Analyzers
[Templates.nuget]: https://www.nuget.org/packages/MassTransit.Templates
@@ -132,10 +135,12 @@ Logo Design by _The Agile Badger_
[Quartz.nuget]: https://www.nuget.org/packages/MassTransit.Quartz
[ActiveMQ.nuget]: https://www.nuget.org/packages/MassTransit.ActiveMQ
+[AmazonS3.nuget]: https://www.nuget.org/packages/MassTransit.AmazonS3
[AmazonSQS.nuget]: https://www.nuget.org/packages/MassTransit.AmazonSQS
[AzureSbCore.nuget]: https://www.nuget.org/packages/MassTransit.Azure.ServiceBus.Core
-[Grpc.nuget]: https://www.nuget.org/packages/MassTransit.Grpc
[RabbitMQ.nuget]: https://www.nuget.org/packages/MassTransit.RabbitMQ
+[PostgreSQL.nuget]: https://nuget.org/packages/MassTransit.SqlTransport.PostgreSQL/
+[SqlServer.nuget]: https://nuget.org/packages/MassTransit.SqlTransport.SqlServer/
[EventHubs.nuget]: https://www.nuget.org/packages/MassTransit.WebJobs.EventHubs
[AzureFunc.nuget]: https://www.nuget.org/packages/MassTransit.WebJobs.ServiceBus
diff --git a/SECURITY.md b/SECURITY.md
index 370ed6bde95..7b2681454f2 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -6,8 +6,8 @@ MassTransit supports the current major version only. If issues or vulnerabilitie
| Version | Supported |
| ------- | ------------------ |
-| 7.x | :white_check_mark: |
-| < 7.0 | :x: |
+| 8.x | :white_check_mark: |
+| < 8.0 | :x: |
## Reporting a Vulnerability
diff --git a/doc/app.config.ts b/doc/app.config.ts
index 77206442745..ff8c61eee17 100644
--- a/doc/app.config.ts
+++ b/doc/app.config.ts
@@ -23,7 +23,7 @@ export default defineAppConfig({
},
footer: {
credits: {
- text: 'Copyright 2023 Chris Patterson',
+ text: 'Copyright 2024 Chris Patterson',
href: 'https://masstransit.io',
icon: 'IconMassTransit'
},
diff --git a/doc/components/global/Mermaid.vue b/doc/components/global/Mermaid.vue
deleted file mode 100644
index bb74ceb011d..00000000000
--- a/doc/components/global/Mermaid.vue
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
-
-
diff --git a/doc/content/2.quick-starts/0.index.md b/doc/content/2.quick-starts/0.index.md
index 34c6ebe647c..d094ce73cf8 100755
--- a/doc/content/2.quick-starts/0.index.md
+++ b/doc/content/2.quick-starts/0.index.md
@@ -37,5 +37,12 @@ Transports
#description
Requires an AWS account
::
+
+ ::card
+ #title
+ [PostgreSQL](/quick-starts/postgresql)
+ #description
+ For smaller setups
+ ::
::
diff --git a/doc/content/2.quick-starts/1.in-memory.md b/doc/content/2.quick-starts/1.in-memory.md
index 63a50530978..f1661060581 100644
--- a/doc/content/2.quick-starts/1.in-memory.md
+++ b/doc/content/2.quick-starts/1.in-memory.md
@@ -13,7 +13,7 @@ This example requires a functioning installation of the .NET Runtime and SDK (at
MassTransit includes project and item [templates](/quick-starts/templates) simplifying the creation of new projects. Install the templates by executing the command below in the console. A video introducing the templates is available on [YouTube](https://youtu.be/nYKq61-DFBQ).
```
-dotnet new --install MassTransit.Templates
+dotnet new install MassTransit.Templates
```
## Create the Project
diff --git a/doc/content/2.quick-starts/2.rabbitmq.md b/doc/content/2.quick-starts/2.rabbitmq.md
index b6b135c0985..4809e227a70 100644
--- a/doc/content/2.quick-starts/2.rabbitmq.md
+++ b/doc/content/2.quick-starts/2.rabbitmq.md
@@ -16,7 +16,10 @@ This tutorial will get you from zero to up and running with [RabbitMQ](/document
The following instructions assume you are starting from a completed [In-Memory Quick Start](/quick-starts/in-memory)
::
-This example requires a functioning installation of the .NET Runtime and SDK (at least 6.0) and a functioning installation of _Docker_ with _Docker Compose_ support enabled.
+This example requires the following:
+
+- a functioning installation of the .NET Runtime and SDK (at least 6.0)
+- a functioning installation of _Docker_ with _Docker Compose_ support enabled.
## Run RabbitMQ
@@ -99,3 +102,8 @@ info: GettingStarted.MessageConsumer[0]
At this point the service is connecting to RabbitMQ on _localhost_ and publishing messages which are received by the consumer.
:tada:
+
+## What is next
+
+- [RabbitMQ Transport overview](/documentation/transports/rabbitmq)
+- [RabbitMQ Transport configuration](/documentation/configuration/transports/rabbitmq)
diff --git a/doc/content/2.quick-starts/3.azure-service-bus.md b/doc/content/2.quick-starts/3.azure-service-bus.md
index ab667d9921b..e5717cb996a 100644
--- a/doc/content/2.quick-starts/3.azure-service-bus.md
+++ b/doc/content/2.quick-starts/3.azure-service-bus.md
@@ -94,3 +94,9 @@ info: GettingStarted.MessageConsumer[0]
At this point, the service is connecting to Azure Service Bus and publishing messages which are received by the consumer.
:tada:
+
+## What is next
+
+- [Azure Service Bus transport overview](/documentation/transports/azure-service-bus)
+- [Azure Service Bus transport configuration](/documentation/configuration/transports/azure-service-bus)
+- [Azure Service Bus sample](https://github.com/MassTransit/Sample-AzureServiceBus)
diff --git a/doc/content/2.quick-starts/4.amazon-sqs.md b/doc/content/2.quick-starts/4.amazon-sqs.md
index 13f4a3c9f5d..b453ab2b7c0 100644
--- a/doc/content/2.quick-starts/4.amazon-sqs.md
+++ b/doc/content/2.quick-starts/4.amazon-sqs.md
@@ -137,3 +137,8 @@ info: GettingStarted.MessageConsumer[0]
At this point the service is connecting to Amazon SQS/SNS in the region `us-east-1` and publishing messages which are received by the consumer.
:tada:
+
+## What is next
+
+- [AmazonSQS transport overview](/documentation/transports/amazon-sqs)
+- [AmazonSQS transport configuration](/documentation/configuration/transports/amazon-sqs)
diff --git a/doc/content/2.quick-starts/5.postgresql.md b/doc/content/2.quick-starts/5.postgresql.md
new file mode 100644
index 00000000000..8bdfaf1776f
--- /dev/null
+++ b/doc/content/2.quick-starts/5.postgresql.md
@@ -0,0 +1,130 @@
+---
+navigation.title: PostgreSQL
+---
+
+# PostgreSQL Quick Start
+
+> This tutorial will get you from zero to up and running with [SQL](/documentation/transports/sql) and MassTransit.
+
+> Walkthrough Video TBD
+
+- The source for this sample is available [on GitHub](https://github.com/MassTransit/Sample-GettingStarted).
+
+## Prerequisites
+
+::alert{type="info"}
+The following instructions assume you are starting from a completed [In-Memory Quick Start](/quick-starts/in-memory)
+::
+
+This example requires the following:
+
+- a functioning installation of the dotnet runtime and sdk (at least 6.0)
+- a functioning installation of _Docker_ with _Docker Compose_ support enabled.
+
+## Run PostgreSQL
+
+For this quick start, we recommend running the preconfigured [official Docker image of Postgres](https://hub.docker.com/_/postgres).
+
+```bash
+$ docker run -p 5432:5432 postgres
+```
+
+If you are running on an ARM platform
+
+```bash
+$ docker run --platform linux/arm64 -p 5432:5432 postgres
+```
+
+Once its up and running you can use your preferred tool to browse into the database.
+
+## Configure PostgreSQL
+
+Add the _MassTransit.SqlTransport.PostgreSQL_ package to the project.
+
+```bash
+$ dotnet add package MassTransit.SqlTransport.PostgreSQL
+```
+
+### Edit Program.cs
+
+Change _UsingInMemory_ to _UsingPostgres_ as shown below.
+
+```csharp
+public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureServices((hostContext, services) =>
+ {
+ services.AddOptions().Configure(options =>
+ {
+ options.Host = "localhost";
+ options.Database = "sample";
+ options.Schema = "transport";
+ options.Role = "transport";
+ options.Username = "masstransit";
+ options.Password = "H4rd2Gu3ss!";
+
+ // credentials to run migrations
+ options.AdminUsername = "migration-user";
+ options.AdminPassword = "H4rderTooGu3ss!!";
+ });
+ // MassTransit will run the migrations on start up
+ services.AddPostgresMigrationHostedService();
+ services.AddMassTransit(x =>
+ {
+ // elided...
+
+ x.UsingPostgres((context,cfg) =>
+ {
+ cfg.ConfigureEndpoints(context);
+ });
+ });
+
+ services.AddHostedService();
+ });
+```
+
+| Setting | Description |
+|-----------------|-------------------------------------------------------------------------------------------|
+| `Host` | The host to connect to. We are using `localhost` to connect to the docker container |
+| `Port` | We are using the default `5432`, so we aren't setting it. |
+| `Database` | The name of the database to connect to |
+| `Schema` | The schema to place the tables and functions inside of |
+| `Role` | the role to assign for all created tables, functions, etc. |
+| `Username` | The username of the user to login as for normal operations |
+| `Password` | The password of the user to login as for normal operations |
+| `AdminUsername` | The username of the admin user to login as when running migration commands |
+| `AdminPassword` | The password of the admin user to login as when running migration commands |
+
+
+## Run the Project
+
+```bash
+$ dotnet run
+```
+
+The output should have changed to show the message consumer generating the output (again, press Control+C to exit). Notice that the bus address now starts with _db_.
+
+```
+Building...
+info: MassTransit[0]
+ Configured endpoint Message, Consumer: GettingStarted.MessageConsumer
+info: Microsoft.Hosting.Lifetime[0]
+ Application started. Press Ctrl+C to shut down.
+info: Microsoft.Hosting.Lifetime[0]
+ Hosting environment: Development
+info: Microsoft.Hosting.Lifetime[0]
+ Content root path: /Users/chris/Garbage/start/GettingStarted
+info: MassTransit[0]
+ Bus started: db://localhost/
+info: GettingStarted.MessageConsumer[0]
+ Received Text: The time is 3/24/2021 12:11:10 PM -05:00
+```
+
+At this point the service is connecting to PostgreSQL on _localhost_ and publishing messages which are received by the consumer.
+
+:tada:
+
+## What is next
+
+- [SQL transport overview](/documentation/transports/sql)
+- [SQL transport sample](https://github.com/MassTransit/Sample-DbTransport)
diff --git a/doc/content/2.quick-starts/templates.md b/doc/content/2.quick-starts/templates.md
index 5a3173cd337..0abd1368f2c 100644
--- a/doc/content/2.quick-starts/templates.md
+++ b/doc/content/2.quick-starts/templates.md
@@ -7,7 +7,7 @@ A video introducing the templates is available on [YouTube](https://youtu.be/nYK
## Installation
```sh
-dotnet new --install MassTransit.Templates
+dotnet new install MassTransit.Templates
```
One installed, typing `dotnet new` will display the available templates:
diff --git a/doc/content/3.documentation/1.concepts/0.index.md b/doc/content/3.documentation/1.concepts/0.index.md
index 43c9532bbdb..5bdf786e0e6 100644
--- a/doc/content/3.documentation/1.concepts/0.index.md
+++ b/doc/content/3.documentation/1.concepts/0.index.md
@@ -6,3 +6,15 @@ navigation.title: Overview
When learning MassTransit, it is a good idea to understand messaging concepts and terminology. To ensure that you are on the right path when looking at a class or interface, review these concepts when working with MassTransit.
+## The Basics
+
+- [Messages](/documentation/concepts/messages)
+- [Consumers](/documentation/concepts/consumers)
+- [Producers](/documentation/concepts/producers)
+- [Exceptions](/documentation/concepts/exceptions)
+- [Testing](/documentation/concepts/testing)
+
+## Intermediate
+
+- [Requests](/documentation/concepts/requests)
+- [Routing Slips](/documentation/concepts/routing-slips)
diff --git a/doc/content/3.documentation/1.concepts/1.messages.md b/doc/content/3.documentation/1.concepts/1.messages.md
index 2d75f1c8328..99ff601cd04 100644
--- a/doc/content/3.documentation/1.concepts/1.messages.md
+++ b/doc/content/3.documentation/1.concepts/1.messages.md
@@ -54,9 +54,9 @@ namespace Company.Application.Contracts
}
```
-When defining a message type using an interface, MassTransit will create a dynamic class implementing the interface for serialization, allowing the interface with get-only properties to be presented to the consumer. To create an interface message, use a [message initializer](/documentation/concepts/producers##message-initialization).
+When defining a message type using an interface, MassTransit will create a dynamic class implementing the interface for serialization, allowing the interface with get-only properties to be presented to the consumer. To create an interface message, use a [message initializer](/documentation/concepts/producers#message-initialization).
-### Classes
+### Classes
```csharp
namespace Company.Application.Contracts
@@ -85,11 +85,12 @@ A common mistake when engineers are new to messaging is to create a base class f
## Message Attributes
-| Attribute | Description |
-|:----------------------------|:------------------------------------------------------------------------------|
-| EntityName | The exchange or topic name |
-| ExcludeFromTopology | Don't create an exchange or topic unless it is directly consumed or published |
-| ExcludeFromImplementedTypes | Don't create a middleware filter for the message type |
+| Attribute | Description |
+|:---------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------|
+| [EntityName](/documentation/configuration/topology/message#entityname) | The exchange or topic name |
+| [ExcludeFromTopology](/documentation/configuration/topology/message#excludefromtopology) | Don't create an exchange or topic unless it is directly consumed or published |
+| [ExcludeFromImplementedTypes](/documentation/configuration/topology/message#excludefromimplementedtypes) | Don't create a middleware filter for the message type |
+| [MessageUrn](/documentation/configuration/topology/message#messageurn) | The message urn |
## Message Names
@@ -97,7 +98,7 @@ There are two main message types, _events_ and _commands_. When choosing a name
### Commands
-A command tells _a_ service to do something, and typically a command should only be consumed by a single consumer. If you have a command, such as `SubmitOrder`, then you should have only one consumer that implements `IConsumer` or one saga state machine with the `Event` configured. By maintaining the one-to-one relationship of a command to a consumer, commands may by _published_ and they will be automatically routed to the consumer.
+A command tells _a_ service to do something, and typically a command should only be consumed by a single consumer. If you have a command, such as `SubmitOrder`, then you should have only one consumer that implements `IConsumer` or one saga state machine with the `Event` configured. By maintaining the one-to-one relationship of a command to a consumer, commands may be _published_ and they will be automatically routed to the consumer.
When using RabbitMQ, there is _no additional overhead_ using this approach. However, both Azure Service Bus and Amazon SQS have a more complicated routing structure and because of that structure, additional charges may be incurred since messages need to be forwarded from topics to queues. For low- to medium-volume message loads this isn't a major concern, but for larger high-volume loads it may be preferable to _[send](/documentation/concepts/producers#send)_ (using `Send`) commands directly to the queue to reduce latency and cost.
@@ -211,7 +212,7 @@ When defining message contracts, what follows is general guidance based upon yea
::list{type="success"}
- Use records, define properties as `public` and specify `{ get; init; }` accessors. Create messages using the constructor/object initializer or a [message initializer](producers#message-initialization).
- - Use interfaces, specify only `{ get; }` accessors. Create messages using message initializers and use the Roslyn Analyzer to identify missing or incompatible properties.
+ - Use interfaces, specify only `{ get; }` accessors. Create messages using message initializers and use the Roslyn Analyzer to identify missing or incompatible properties.
- Limit the use of inheritance, pay attention to polymorphic message routing. A message type containing a dozen interfaces is a bit annoying to untangle if you need to delve deep into message routing to troubleshoot an issue.
- Class inheritance has the same guidance as interfaces, but with more caution.
::
@@ -223,3 +224,44 @@ When defining message contracts, what follows is general guidance based upon yea
- A big base class may cause pain down the road as changes are made, particularly when supporting multiple message versions.
::
+### Message Inheritance
+
+::alert{type="info"}
+Message design is not object-oriented design.
+::
+
+This concept comes up often enough that it warrants its own special section. By design, MassTransit treats your classes, records, and interfaces as a "contract".
+
+An example, let's say that you have a message that is defined by the dotnet class below
+
+```csharp
+public record SubmitOrder
+{
+ public string Sku { get; init; }
+ public int Quantity { get; init; }
+}
+```
+
+You want all of your messages to have a common set of properties so you try and do this.
+
+```csharp
+public record CoreEvent
+{
+ public string User { get; init; }
+}
+
+public record SubmitOrder :
+ CoreEvent
+{
+ public string Sku { get; init; }
+ public int Quantity { get; init; }
+}
+```
+
+If you try and consume a `Batch` and expect to get a variety of types, one of which would be `SubmitOrder`. In OOP land, that makes all the sense in the world, but in MassTransit contract design it does not. The application has said that it cares about batches of `CoreEvent` so it will only get back the single property `User`. This is not a symptom of using System.Text.Json, this has been the standard behavior of MassTransit since day one, even when using Netwonsoft.Json. MassTransit will always respect the contract that has been designed.
+
+If you want to have a standard set of properties available, by all means use a base class, or bundle them up into a single property, our preference. If you want to subscribe to all implementations of class, then you will need to subscribe to all implementations of a class.
+
+
+
+
diff --git a/doc/content/3.documentation/1.concepts/2.consumers.md b/doc/content/3.documentation/1.concepts/2.consumers.md
index b2b6896a6bc..aac90104967 100644
--- a/doc/content/3.documentation/1.concepts/2.consumers.md
+++ b/doc/content/3.documentation/1.concepts/2.consumers.md
@@ -33,7 +33,7 @@ class SubmitOrderConsumer :
}
```
-To add a consumer and automatically configure a receive endpoint for the consumer, call one of the [_AddConsumer_](/documentation/configuration/bus/consumers) methods and call _ConfigureEndpoints_ as shown below.
+To add a consumer and automatically configure a receive endpoint for the consumer, call one of the [_AddConsumer_](/documentation/configuration/consumers) methods and call _ConfigureEndpoints_ as shown below.
```csharp
services.AddMassTransit(x =>
diff --git a/doc/content/3.documentation/1.concepts/4.exceptions.md b/doc/content/3.documentation/1.concepts/4.exceptions.md
index e1ac33bb8b7..c5484aae8cd 100644
--- a/doc/content/3.documentation/1.concepts/4.exceptions.md
+++ b/doc/content/3.documentation/1.concepts/4.exceptions.md
@@ -62,11 +62,14 @@ With this consumer, an `ADOException` can be thrown, say there is a deadlock or
services.AddMassTransit(x =>
{
x.AddConsumer();
-
- x.UsingRabbitMq((context,cfg) =>
+
+ x.AddConfigureEndpointsCallback((context,name,cfg) =>
{
cfg.UseMessageRetry(r => r.Immediate(5));
+ });
+ x.UsingRabbitMq((context,cfg) =>
+ {
cfg.ConfigureEndpoints(context);
});
});
@@ -97,7 +100,7 @@ services.AddMassTransit(x =>
});
```
-MassTransit retry filters execute in memory and maintain a _lock_ on the message. As such, they should only be used to handle short, transient error conditions. Setting a retry interval of an hour would fall into the category of _bad things_. To retry messages after longer waits, look at the next section on redelivering messages.
+MassTransit retry filters execute in memory and maintain a _lock_ on the message. As such, they should only be used to handle short, transient error conditions. Setting a retry interval of an hour would fall into the category of _bad things_. To retry messages after longer waits, look at the next section on redelivering messages. For example, if a consumer with a concurrency limit of 5 and a retry interval of one hour consumes 5 messages that causes retries, the consumer will be effectively stalled for a whole hour as all the concurrent message slots are in use waiting for the retry interval.
## Retry Configuration
@@ -113,14 +116,14 @@ Learn more about message retry in this video
When configuring message retry, there are several retry policies available, including:
-| Policy | Description |
-| :-- | :-- |
-| None | No retry
-| Immediate | Retry immediately, up to the retry limit
-| Interval | Retry after a fixed delay, up to the retry limit
-| Intervals | Retry after a delay, for each interval specified
-| Exponential | Retry after an exponentially increasing delay, up to the retry limit
-| Incremental | Retry after a steadily increasing delay, up to the retry limit
+| Policy | Description |
+|:------------|:---------------------------------------------------------------------|
+| None | No retry |
+| Immediate | Retry immediately, up to the retry limit |
+| Interval | Retry after a fixed delay, up to the retry limit |
+| Intervals | Retry after a delay, for each interval specified |
+| Exponential | Retry after an exponentially increasing delay, up to the retry limit |
+| Incremental | Retry after a steadily increasing delay, up to the retry limit |
Each policy has configuration settings which specifies the expected behavior.
@@ -189,12 +192,15 @@ To use delayed redelivery, ensure the transport is properly configured. RabbitMQ
services.AddMassTransit(x =>
{
x.AddConsumer();
-
- x.UsingRabbitMq((context, cfg) =>
+
+ x.AddConfigureEndpointsCallback((context,name,cfg) =>
{
cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30)));
cfg.UseMessageRetry(r => r.Immediate(5));
+ });
+ x.UsingRabbitMq((context, cfg) =>
+ {
cfg.ConfigureEndpoints(context);
});
});
@@ -203,7 +209,8 @@ services.AddMassTransit(x =>
Now, if the initial 5 immediate retries fail (the database is really, really down), the message will retry an additional three times after 5, 15, and 30 minutes. This could mean a total of 15 retry attempts (on top of the initial 4 attempts prior to the retry/redelivery filters taking control).
::alert{type="info"}
-MassTransit also supports scheduled redelivery using the `UseScheduledRedelivery` configuration method. Scheduled redelivery requires the use of a message scheduler, which can be configured to use the message transport or Quartz.NET/Hangfire. The configuration is similar, just ensure the scheduler is properly configured.
+MassTransit also supports scheduled redelivery using the `UseScheduledRedelivery` configuration method. Scheduled redelivery requires the use of a message scheduler, which can be configured to use the message transport or Quartz.NET/Hangfire. The configuration is similar, just ensure the scheduler is properly configured.
+However, in most cases using `UseDelayedRedelivery` (as configured above) is preferred to avoid overloading the scheduler with delayed redeliveries that typically have short redelivery times, leaving the scheduler free to do things like actual scheduling of messages.
::
## Outbox
@@ -216,13 +223,16 @@ To configure the outbox with redelivery and retry:
services.AddMassTransit(x =>
{
x.AddConsumer();
-
- x.UsingRabbitMq((context, cfg) =>
+
+ x.AddConfigureEndpointsCallback((context,name,cfg) =>
{
cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30)));
cfg.UseMessageRetry(r => r.Immediate(5));
- cfg.UseInMemoryOutbox();
+ cfg.UseInMemoryOutbox(context);
+ });
+ x.UsingRabbitMq((context, cfg) =>
+ {
cfg.ConfigureEndpoints(context);
});
});
@@ -247,7 +257,7 @@ services.AddMassTransit(x =>
{
c.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30)));
c.UseMessageRetry(r => r.Immediate(5));
- c.UseInMemoryOutbox();
+ c.UseInMemoryOutbox(context);
});
});
});
@@ -279,7 +289,7 @@ If the message headers specify a `FaultAddress`, the fault is sent directly to t
### Consuming Faults
-Developers may want to do something with faults, such as updating an operational dashboard. To observe faults separate of the consumer that caused the fault to be produced, a consumer can consume fault messages the same as any other message.
+Developers may want to do something with faults, such as updating an operational dashboard or notifying a support team. To observe faults separate of the consumer that caused the fault to be produced, a consumer can consume fault messages the same as any other message.
```csharp
public class DashboardFaultConsumer :
@@ -302,6 +312,18 @@ Event(() => SubmitOrderFaulted, x => x
public Event> SubmitOrderFaulted { get; private set; }
```
+### Managing Faults
+
+In any production system faults will happen and you should be prepared to manage these. Faults need to be inspected to identify why the messages failed and once the cause for the problem has been identified and resolved, the messages should be retried.
+
+You can use broker built-in tools like RabbitMQ Management UI, Azure portal Service Bus Explorer, Amazon AWS SQS web console, or Cogent Queue Explorer to inspect the Faults. Once the reason for the fault has been resolved, you can use the tool to extract the original message and send it back to the original consumer. In this manner, messages that fail Retries and Redelivery can still be successfully processed at a later stage.
+
+Also see:
+
+- [Retrying messages with RabbitMQ](/documentation/transports/rabbitmq#retrying-messages)
+- [Retrying messages with Azure Service Bus](/documentation/transports/azure-service-bus#retrying-messages)
+- [Retrying messages with Amazon SQS](/documentation/transports/amazon-sqs#retrying-messages)
+
## Error Pipe
By default, MassTransit will move faulted messages to the *_error* queue. This behavior can be customized for each receive endpoint.
diff --git a/doc/content/3.documentation/1.concepts/5.requests.md b/doc/content/3.documentation/1.concepts/5.requests.md
deleted file mode 100644
index f42caed782e..00000000000
--- a/doc/content/3.documentation/1.concepts/5.requests.md
+++ /dev/null
@@ -1,366 +0,0 @@
-# Requests
-
-Request/response is a commonly used message pattern where one service sends a request to another service, continuing after the response is received. In a distributed system, this can increase the latency of an application since the service may be hosted in another process, on another machine, or may even be a remote service in another network. While in many cases it is best to avoid request/response use in distributed applications, particularly when the request is a command, it is often necessary and preferred over more complex solutions.
-
-In MassTransit, developers use a _request client_ to send or publish requests and wait for a response. The request client is asynchronous, and supports use of the _await_ keyword since it returns a _Task_.
-
-## Message Contracts
-
-To use the request client, create two message contracts, one for the request and one for the response.
-
-```csharp
-public record CheckOrderStatus
-{
- public string OrderId { get; init; }
-}
-
-public record OrderStatusResult
-{
- public string OrderId { get; init; }
- public DateTime Timestamp { get; init; }
- public short StatusCode { get; init; }
- public string StatusText { get; init; }
-}
-```
-
-## Request Consumer
-
-Request messages can be handled by any consumer type, including consumers, sagas, and routing slips. In this case, the consumer below consumes the _CheckOrderStatus_ message and responds with the _OrderStatusResult_ message.
-
-```csharp
-public class CheckOrderStatusConsumer :
- IConsumer
-{
- readonly IOrderRepository _orderRepository;
-
- public CheckOrderStatusConsumer(IOrderRepository orderRepository)
- {
- _orderRepository = orderRepository;
- }
-
- public async Task Consume(ConsumeContext context)
- {
- var order = await _orderRepository.Get(context.Message.OrderId);
- if (order == null)
- throw new InvalidOperationException("Order not found");
-
- await context.RespondAsync(new
- {
- OrderId = order.Id,
- order.Timestamp,
- order.StatusCode,
- order.StatusText
- });
- }
-}
-```
-
-If the _OrderId_ is found in the repository, an _OrderStatusResult_ message will be sent to the response address included with the request. The waiting request client will handle the response and complete the returned _Task_ allowing the requesting application to continue.
-
-If the _OrderId_ was not found, the consumer throws an exception. MassTransit catches the exception, generates a `Fault` message, and sends it to the response address. The request client handles the fault message and throws a _RequestFaultException_ via the awaited _Task_ containing the exception detail.
-
-## Request Client
-
-To use the request client, add the request client as a dependency as shown in the example API controller below.
-
-```csharp
-public class RequestController :
- Controller
-{
- IRequestClient _client;
-
- public RequestController(IRequestClient client)
- {
- _client = client;
- }
-
- [HttpGet("{orderId}")]
- public async Task Get(string orderId, CancellationToken cancellationToken)
- {
- var response = await _client.GetResponse(new { orderId }, cancellationToken);
-
- return Ok(response.Message);
- }
-}
-```
-
-The controller method will send the request and return the order status after the response has been received.
-
-If the _cancellationToken_ passed to _GetResponse_ is canceled, the request client will stop waiting for a response. However, the request message produced remains in the queue until it is consumed or the message time-to-live expires. By default, the message time-to-live is set to the request timeout (which defaults to 30 seconds).
-
-## Client Configuration
-
-A request client can be resolved using dependency injection for any valid message type, no configuration is required. By default, request messages are _published_ and should be consumed by only one consumer/receive endpoint connected to the message broker. Multiple consumers connected to the same receive endpoint are fine, requests will be load balanced across the connected consumers.
-
-To configure the request client for a message type, add the request client to the configuration explicitly.
-
-```csharp
-services.AddMassTransit(x =>
-{
- // configure the consumer on a specific endpoint address
- x.AddConsumer()
- .Endpoint(e => e.Name = 'order-status');
-
- // Sends the request to the specified address, instead of publishing it
- x.AddRequestClient(new Uri("exchange:order-status"));
-
- x.UsingInMemory((context, cfg) =>
- {
- cfg.ConfigureEndpoints(context);
- }));
-});
-```
-
-::alert{type="success"}
-The request client receives responses on the bus endpoint, which is a temporary queue created by MassTransit. The queue is only created after the bus has started just before the first request. MassTransit adds a hosted service to the service collection that starts and stops the bus. If the request client is timing out (and therefore throwing a _RequestTimeoutException_), make sure that the bus is being started (check the logs, etc.).
-::
-
-## Request Headers
-
-To create a request and add a header to the `SendContext`, one option is to add an execute filter to the request pipeline.
-
-```csharp
-await client.GetResponse(new GetOrderStatus{ OrderId = orderId },
- x => x.UseExecute(context => context.Headers.Set("tenant-id", "some-value")));
-```
-
-Another option is to use the _object values_ overload, which uses a message initializer, to specify the header value. Learn more about [message initializers](/documentation/concepts/producers#message-initializers) in the _Concepts_ section.
-
-```csharp
-await client.GetResponse(new
-{
- orderId,
- __Header_Tenant_Id = "some-value"
-});
-```
-
-
-## Multiple Response Types
-
-Another powerful feature with the request client is the ability support multiple (such as positive and negative) result types. For example, adding an `OrderNotFound` response type to the consumer as shown eliminates throwing an exception since a missing order isn't really a fault.
-
-```csharp
-public class CheckOrderStatusConsumer :
- IConsumer
-{
- public async Task Consume(ConsumeContext context)
- {
- var order = await _orderRepository.Get(context.Message.OrderId);
- if (order == null)
- await context.RespondAsync(context.Message);
- else
- await context.RespondAsync(new
- {
- OrderId = order.Id,
- order.Timestamp,
- order.StatusCode,
- order.StatusText
- });
- }
-}
-```
-
-The client can now wait for multiple response types (in this case, two) by using a little tuple magic.
-
-```csharp
-var response = await client.GetResponse(new { OrderId = id});
-
-if (response.Is(out Response responseA))
-{
- // do something with the order
-}
-else if (response.Is(out Response responseB))
-{
- // the order was not found
-}
-```
-
-This cleans up the processing, an eliminates the need to catch a `RequestFaultException`.
-
-It's also possible to use some of the switch expressions via deconstruction, but this requires the response variable to be explicitly specified as `Response`.
-
-```csharp
-Response response = await client.GetResponse(new { OrderId = id});
-
-// Using a regular switch statement
-switch (response)
-{
- case (_, OrderStatusResult a) responseA:
- // order found
- break;
- case (_, OrderNotFound b) responseB:
- // order not found
- break;
-}
-
-// Or using a switch expression
-var accepted = response switch
-{
- (_, OrderStatusResult a) => true,
- (_, OrderNotFound b) => false,
- _ => throw new InvalidOperationException()
-};
-```
-
-## Accept Response Types
-
-The request client sets a message header, `MT-Request-AcceptType`, that contains the response types supported by the request client. This allows the request consumer to determine if the client can handle a response type, which can be useful as services evolve and new response types may be added to handle new conditions. For instance, if a consumer adds a new response type, such as `OrderAlreadyShipped`, if the response type isn't supported an exception may be thrown instead.
-
-To see this in code, check out the client code:
-
-```csharp
-var response = await client.GetResponse(new CancelOrder());
-
-if (response.Is(out Response canceled))
-{
- return Ok();
-}
-else if (response.Is(out Response responseB))
-{
- return NotFound();
-}
-```
-
-The original consumer, prior to adding the new response type:
-
-```csharp
-public async Task Consume(ConsumeContext context)
-{
- var order = _repository.Load(context.Message.OrderId);
- if(order == null)
- {
- await context.ResponseAsync(new { context.Message.OrderId });
- return;
- }
-
- order.Cancel();
-
- await context.RespondAsync(new { context.Message.OrderId });
-}
-```
-
-Now, the new consumer that checks if the order has already shipped:
-
-```csharp
-public async Task Consume(ConsumeContext context)
-{
- var order = _repository.Load(context.Message.OrderId);
- if(order == null)
- {
- await context.ResponseAsync(new { context.Message.OrderId });
- return;
- }
-
- if(order.HasShipped)
- {
- if (context.IsResponseAccepted())
- {
- await context.RespondAsync(new { context.Message.OrderId, order.ShipDate });
- return;
- }
- else
- throw new InvalidOperationException("The order has already shipped"); // to throw a RequestFaultException in the client
- }
-
- order.Cancel();
-
- await context.RespondAsync(new { context.Message.OrderId });
-}
-```
-
-This way, the consumer can check the request client response types and act accordingly.
-
-::alert{type="success"}
-For backwards compatibility, if the new `MT-Request-AcceptType` header is not found, `IsResponseAccepted` will return true for all message types.
-::
-
-## Concurrent Requests
-
-If there were multiple requests to be performed, it is easy to wait on all results at the same time, benefiting from the concurrent operation.
-
-```csharp
-public class RequestController :
- Controller
-{
- IRequestClient _clientA;
- IRequestClient _clientB;
-
- public RequestController(IRequestClient clientA, IRequestClient clientB)
- {
- _clientA = clientA;
- _clientB = clientB;
- }
-
- public async Task Get()
- {
- var resultA = _clientA.GetResponse(new RequestA());
- var resultB = _clientB.GetResponse(new RequestB());
-
- await Task.WhenAll(resultA, resultB);
-
- var a = await resultA;
- var b = await resultB;
-
- var model = new Model(a.Message, b.Message);
-
- return View(model);
- }
-}
-```
-
-The power of concurrency, for the win!
-
-## Request Client Details
-
-> The internals are documented for understanding, but what follows is optional reading. The above container-based configuration handles all the details to ensure the property context is used.
-
-The request client is composed of two parts, a client factory, and a request client. The client factory is created from the bus, or a connected endpoint, and has the interface below (some overloads are omitted, but you get the idea).
-
-```csharp
-public interface IClientFactory
-{
- IRequestClient CreateRequestClient(ConsumeContext context, Uri destinationAddress, RequestTimeout timeout);
-
- IRequestClient CreateRequestClient(Uri destinationAddress, RequestTimeout timeout);
-
- RequestHandle CreateRequest(T request, Uri destinationAddress, CancellationToken cancellationToken, RequestTimeout timeout);
-
- RequestHandle CreateRequest(ConsumeContext context, T request, Uri destinationAddress, CancellationToken cancellationToken, RequestTimeout timeout);
-}
-```
-
-As shown, the client factory can create a request client, or it can create a request directly. There are advantages to each approach, although it's typically best to create a request client and use it if possible. If a consumer is sending the request, a new client should be created for each message (and is handled automatically if you're using a dependency injection container and the container registration methods).
-
-To create a client factory, call `bus.CreateClientFactory` or `host.CreateClientFactory` -- after the bus has been started.
-
-The request client can be used to create requests (returning a `RequestHandle`, which must be disposed after the request completes) or it can be used directly to send a request and get a response (asynchronously, of course).
-
-> Using `Create` returns a request handle, which can be used to set headers and other attributes of the request before it is sent.
-
-```csharp
-public interface IRequestClient
- where TRequest : class
-{
- RequestHandle Create(TRequest request, CancellationToken cancellationToken, RequestTimeout timeout);
-
- Task> GetResponse(TRequest request, CancellationToken cancellationToken, RequestTimeout timeout);
-}
-```
-
-> For `RequestTimeout` three options are available, `None`, `Default`, and a factory with `RequestTimeout.After`. `None` would never be recommended since it would essentially wait forever for a response. There is always a relevant timeout, or you're using the wrong pattern.
-
-## Sending a Request
-
-To create a request client, and use it to make a standalone request (not from a consumer, API controller, etc.):
-
-```csharp
-var serviceAddress = new Uri("rabbitmq://localhost/check-order-status");
-var client = bus.CreateRequestClient(serviceAddress);
-
-var response = await client.GetResponse(new { OrderId = id});
-```
-
-The response type, `Response` includes the _MessageContext_ from when the response was received, providing access to the message properties (such as `response.ConversationId`) and headers (`response.Headers`).
-
-
-
diff --git a/doc/content/3.documentation/1.concepts/5.testing.md b/doc/content/3.documentation/1.concepts/5.testing.md
new file mode 100644
index 00000000000..20cb9eb5b83
--- /dev/null
+++ b/doc/content/3.documentation/1.concepts/5.testing.md
@@ -0,0 +1,318 @@
+# Testing
+
+MassTransit is an asynchronous framework that enables the development of high-performance and flexible distributed applications. Because of MassTransit's asynchronous underpinning, unit testing consumers, sagas, and routing slip activities can be significantly more complex. To simplify the creation of unit and integration tests, MassTransit includes a [Test Harness](/documentation/configuration/test-harness) that simplifies test creation.
+
+## Test Harness Features
+
+- Simplifies configuration for a majority of unit test scenarios
+- Provides an in-memory transport, saga repository, and message scheduler
+- Exposes published, sent, and consumed messages
+- Supports Web Application Factory for testing ASP.NET Applications
+
+## Test Harness Concepts
+
+As stated above, MassTransit is an asynchronous framework. In most cases, developers want to test that message consumption is successful, consumer behavior is as expected, and messages are published and/or sent. Because these actions are performed asynchronously, MassTransit's test harness exposes several asynchronous collections allowing test assertions verifying developer expectations. These asynchronous collections are backed by an over test timer and an inactivity timer, so it's important to use a test harness only once for a given scenario. Multiple test assertions, messages, and behaviors are normal in a given test, but unrelated scenarios should not share a single test harness.
+
+MassTransit's test harness is built around Microsoft's Dependency Injection and is configured using the `AddMassTransitTestHarness` extension method. An test example is shown below, that verifies a `SubmitOrderConsumer` consumes a request and responds to the requester.
+
+```csharp
+[Test]
+public async Task An_example_unit_test()
+{
+ await using var provider = new ServiceCollection()
+ .AddYourBusinessServices() // register all of your normal business services
+ .AddMassTransitTestHarness(x =>
+ {
+ x.AddConsumer();
+ })
+ .BuildServiceProvider(true);
+
+ var harness = provider.GetRequiredService();
+
+ await harness.Start();
+
+ var client = harness.GetRequestClient();
+
+ var response = await client.GetResponse(new
+ {
+ OrderId = InVar.Id,
+ OrderNumber = "123"
+ });
+
+ Assert.IsTrue(await harness.Sent.Any());
+
+ Assert.IsTrue(await harness.Consumed.Any());
+
+ var consumerHarness = harness.GetConsumerHarness();
+
+ Assert.That(await consumerHarness.Consumed.Any());
+
+ // test side effects of the SubmitOrderConsumer here
+}
+```
+
+In the example above, the `AddMassTransitTestHarness` method is used to configure MassTransit, the in-memory transport, and the test harness on the service collection using all the default settings. This simple method is the same as the configuration shown below. The default settings eliminate all the extra code, simplifying the test set up.
+
+```csharp
+.AddMassTransitTestHarness(x =>
+{
+ x.AddDelayedMessageScheduler();
+
+ x.AddConsumer();
+
+ x.UsingInMemory((context, cfg) =>
+ {
+ cfg.UseDelayedMessageScheduler();
+
+ cfg.ConfigureEndpoints(context);
+ });
+})
+```
+
+### Transport Support
+
+MassTransit's test harness can be used with any supported transport, and can also be used write rider integration tests (there is no in-memory rider implementation for unit testing). For example, to create an integration test using RabbitMQ, the RabbitMQ transport can be configured as shown.
+
+```csharp
+.AddMassTransitTestHarness(x =>
+{
+ x.AddConsumer();
+
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ cfg.ConfigureEndpoints(context);
+ });
+})
+```
+
+In this example, RabbitMQ is configured using the default settings (which specifies a broker running on `localhost` using the default username and password of `guest`).
+
+
+### SetTestTimeouts
+
+In the example above, the `await harness.Consumed.Any()` method call waits for the `SubmitOrder` message to be consumed within the time specified by the `TestInactivityTimeout` property of the Test Harness. The default timeout is 30 seconds, and 50 minutes while debugging, configured by the [`TestHarnessOptions`](https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit/Configuration/DependencyInjection/TestHarnessOptions.cs#L9), and can be set:
+
+To set both the overall test timeout and the test inactivity timeout, call the `SetTestTimeouts` method:
+
+```csharp
+cfg.SetTestTimeouts(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(5)));
+```
+
+Either timeout value can be set individually using the appropriately named parameter:
+
+```csharp
+cfg.SetTestTimeouts(testTimeout: TimeSpan.FromSeconds(60));
+
+cfg.SetTestTimeouts(testInactivityTimeout: TimeSpan.FromSeconds(5));
+```
+
+### Message Assertions
+
+To read the messages that were sent you can use the `Select` or `SelectAsync` methods:
+
+```csharp
+var messageSent = await harness.Sent.SelectAsync()
+ .FirstOrDefault();
+
+Assert.AreEqual(orderId, messageSent?.Context.Message.OrderId);
+```
+
+::callout{type="info"}
+#summary
+If you don't limit the number of messages to wait for, e.g. by using the `.FirstOrDefault()`, then the `Select` and `SelectAsync` methods **wait until the `TestTimeout`.** This means **50 minutes** by default while debugging, making the debugging experience unpleasant.
+#content
+For example, using Fluent Assertions the line below would be perfectly fine, but in this case it causes the test to block until the Test Timeout expires.
+
+```csharp
+// Waits for the full TestTimeout
+❌ harness.Sent.Select()
+ .Should().HaveCountGreaterThan(0);
+```
+::
+
+
+## Web Application Factory
+
+MassTransit's test harness can be used with Microsoft's Web Application Factory, allowing unit and/or integration testing of ASP.NET applications.
+
+:sample{sample=web-application-factory}
+
+To configure MassTransit's test harness for use with the Web Application Factory, call `AddMassTransitTestHarness` in the set up as shown below.
+
+```csharp
+await using var application = new WebApplicationFactory()
+ .WithWebHostBuilder(builder =>
+ builder.ConfigureServices(services =>
+ services.AddMassTransitTestHarness()));
+
+var testHarness = application.Services.GetTestHarness();
+
+using var client = application.CreateClient();
+
+var orderId = NewId.NextGuid();
+
+var submitOrderResponse = await client.PostAsync("/Order", JsonContent.Create(new Order
+{
+ OrderId = orderId
+}));
+
+var consumerTestHarness = testHarness.GetConsumerHarness();
+
+Assert.That(await consumerTestHarness.Consumed.Any(x => x.Context.Message.OrderId == orderId), Is.True);
+```
+
+## Examples
+
+The following are examples of using the `TestHarness` to test various components.
+
+### Consumer
+
+To test a consumer using the MassTransit Test Harness:
+
+```csharp
+[Test]
+public async Task ASampleTest()
+{
+ await using var provider = new ServiceCollection()
+ .AddMassTransitTestHarness(cfg =>
+ {
+ cfg.AddConsumer();
+ })
+ .BuildServiceProvider(true);
+
+ var harness = provider.GetRequiredService();
+
+ await harness.Start();
+
+ var client = harness.GetRequestClient();
+
+ await client.GetResponse(new
+ {
+ OrderId = InVar.Id,
+ OrderNumber = "123"
+ });
+
+ Assert.IsTrue(await harness.Sent.Any());
+
+ Assert.IsTrue(await harness.Consumed.Any());
+
+ var consumerHarness = harness.GetConsumerHarness();
+
+ Assert.That(await consumerHarness.Consumed.Any());
+
+ // test side effects of the SubmitOrderConsumer here
+}
+```
+
+### Request / Response
+
+To test a Request using the MassTransit Test Harness:
+
+```csharp
+[Test]
+public async Task ASampleTest()
+{
+ await using var provider = new ServiceCollection()
+ .AddMassTransitTestHarness(cfg =>
+ {
+ cfg.Handler(async cxt =>
+ {
+ await cxt.RespondAsync(new OrderResponse("OK"));
+ });
+ })
+ .BuildServiceProvider(true);
+
+ var harness = provider.GetRequiredService();
+
+ await harness.Start();
+
+ var client = harness.GetRequestClient();
+
+ var response = await client.GetResponse(new SubmitOrder
+ {
+ OrderNumber = "123"
+ });
+
+ Assert.That(response.Message.Status, Is.EqualTo("OK"));
+}
+```
+[Short Addresses](https://masstransit.io/documentation/concepts/producers#short-addresses)
+
+### Saga State Machine
+
+To test a saga state machine using the MassTransit Test Harness:
+
+```csharp
+[Test]
+public async Task ASampleTest()
+{
+ await using var provider = new ServiceCollection()
+ .AddMassTransitTestHarness(cfg =>
+ {
+ cfg.AddSagaStateMachine();
+ })
+ .BuildServiceProvider(true);
+
+ var harness = provider.GetRequiredService();
+
+ await harness.Start();
+
+ var sagaId = Guid.NewGuid();
+ var orderNumber = "ORDER123";
+
+ await harness.Bus.Publish(new OrderSubmitted
+ {
+ CorrelationId = sagaId,
+ OrderNumber = orderNumber
+ });
+
+ Assert.That(await harness.Consumed.Any());
+
+ var sagaHarness = harness.GetSagaStateMachineHarness();
+
+ Assert.That(await sagaHarness.Consumed.Any());
+
+ Assert.That(await sagaHarness.Created.Any(x => x.CorrelationId == sagaId));
+
+ var instance = sagaHarness.Created.ContainsInState(sagaId, sagaHarness.StateMachine, sagaHarness.StateMachine.Submitted);
+ Assert.IsNotNull(instance, "Saga instance not found");
+ Assert.That(instance.OrderNumber, Is.EqualTo(orderNumber));
+
+ Assert.IsTrue(await harness.Published.Any());
+
+ // test side effects of OrderState here
+}
+```
+
+### Routing Slips
+
+To test a routing slip activity using the MassTransit Test Harness:
+
+```csharp
+[Test]
+public async Task ASampleTest()
+{
+ await using var provider = new ServiceCollection()
+ .AddMassTransitTestHarness(cfg =>
+ {
+ cfg.AddActivity();
+ })
+ .BuildServiceProvider(true);
+
+ var harness = provider.GetRequiredService();
+
+ await harness.Start();
+
+ var addr = harness.GetExecuteActivityAddress();
+ var builder = new RoutingSlipBuilder(NewId.NextGuid());
+ builder.AddActivity("test", addr, new {
+ OrderNumber = "ORDER123"
+ })
+
+ await harness.Bus.Execute(builder.Build())
+
+ await harness.Published.Any();
+
+ // test side effects of MyActivity here
+}
+```
diff --git a/doc/content/3.documentation/1.concepts/6.requests.md b/doc/content/3.documentation/1.concepts/6.requests.md
new file mode 100644
index 00000000000..743217dc66c
--- /dev/null
+++ b/doc/content/3.documentation/1.concepts/6.requests.md
@@ -0,0 +1,393 @@
+# Requests
+
+Request/response is a commonly used message pattern where one service sends a request to another service, continuing after the response is received. In a distributed system, this can increase the latency of an application since the service may be hosted in another process, on another machine, or may even be a remote service in another network. While in many cases it is best to avoid request/response use in distributed applications, particularly when the request is a command, it is often necessary and preferred over more complex solutions.
+
+In MassTransit, developers use a _request client_ to send or publish requests and wait for a response. The request client is asynchronous, and supports use of the _await_ keyword since it returns a _Task_.
+
+## Message Contracts
+
+To use the request client, create two message contracts, one for the request and one for the response.
+
+```csharp
+public record CheckOrderStatus
+{
+ public string OrderId { get; init; }
+}
+
+public record OrderStatusResult
+{
+ public string OrderId { get; init; }
+ public DateTime Timestamp { get; init; }
+ public short StatusCode { get; init; }
+ public string StatusText { get; init; }
+}
+```
+
+## Request Consumer
+
+Request messages can be handled by any consumer type, including consumers, sagas, and routing slips. In this case, the consumer below consumes the _CheckOrderStatus_ message and responds with the _OrderStatusResult_ message.
+
+```csharp
+public class CheckOrderStatusConsumer :
+ IConsumer
+{
+ readonly IOrderRepository _orderRepository;
+
+ public CheckOrderStatusConsumer(IOrderRepository orderRepository)
+ {
+ _orderRepository = orderRepository;
+ }
+
+ public async Task Consume(ConsumeContext context)
+ {
+ var order = await _orderRepository.Get(context.Message.OrderId);
+ if (order == null)
+ throw new InvalidOperationException("Order not found");
+
+ await context.RespondAsync(new
+ {
+ OrderId = order.Id,
+ order.Timestamp,
+ order.StatusCode,
+ order.StatusText
+ });
+ }
+}
+```
+
+If the _OrderId_ is found in the repository, an _OrderStatusResult_ message will be sent to the response address included with the request. The waiting request client will handle the response and complete the returned _Task_ allowing the requesting application to continue.
+
+If the _OrderId_ was not found, the consumer throws an exception. MassTransit catches the exception, generates a `Fault` message, and sends it to the response address. The request client handles the fault message and throws a _RequestFaultException_ via the awaited _Task_ containing the exception detail.
+
+## Request Client
+
+To use the request client, add the request client as a dependency as shown in the example API controller below.
+
+```csharp
+public class RequestController :
+ Controller
+{
+ IRequestClient _client;
+
+ public RequestController(IRequestClient client)
+ {
+ _client = client;
+ }
+
+ [HttpGet("{orderId}")]
+ public async Task Get(string orderId, CancellationToken cancellationToken)
+ {
+ var response = await _client.GetResponse(new { orderId }, cancellationToken);
+
+ return Ok(response.Message);
+ }
+}
+```
+
+The controller method will send the request and return the order status after the response has been received.
+
+If the _cancellationToken_ passed to _GetResponse_ is canceled, the request client will stop waiting for a response. However, the request message produced remains in the queue until it is consumed or the message time-to-live expires. By default, the message time-to-live is set to the request timeout (which defaults to 30 seconds).
+
+### Client Configuration
+
+A request client can be resolved using dependency injection for any valid message type, no configuration is required. By default, request messages are _published_ and should be consumed by only one consumer/receive endpoint connected to the message broker. Multiple consumers connected to the same receive endpoint are fine, requests will be load balanced across the connected consumers.
+
+To configure the request client for a message type, add the request client to the configuration explicitly.
+
+```csharp
+services.AddMassTransit(x =>
+{
+ // configure the consumer on a specific endpoint address
+ x.AddConsumer()
+ .Endpoint(e => e.Name = "order-status");
+
+ // Sends the request to the specified address, instead of publishing it
+ x.AddRequestClient(new Uri("exchange:order-status"));
+
+ x.UsingInMemory((context, cfg) =>
+ {
+ cfg.ConfigureEndpoints(context);
+ }));
+});
+```
+
+::alert{type="success"}
+The request client receives responses on the bus endpoint, which is a temporary queue created by MassTransit. The queue is only created after the bus has started just before the first request. MassTransit adds a hosted service to the service collection that starts and stops the bus. If the request client is timing out (and therefore throwing a _RequestTimeoutException_), make sure that the bus is being started (check the logs, etc.).
+::
+
+### Request Headers
+
+To create a request and add a header to the `SendContext`, one option is to add an execute filter to the request pipeline.
+
+```csharp
+await client.GetResponse(new GetOrderStatus{ OrderId = orderId },
+ x => x.UseExecute(context => context.Headers.Set("tenant-id", "some-value")));
+```
+
+Another option is to use the _object values_ overload, which uses a message initializer, to specify the header value. Learn more about [message initializers](/documentation/concepts/producers#message-initializers) in the _Concepts_ section.
+
+```csharp
+await client.GetResponse(new
+{
+ orderId,
+ __Header_Tenant_Id = "some-value"
+});
+```
+
+
+### Multiple Response Types
+
+Another powerful feature with the request client is the ability support multiple (such as positive and negative) result types. For example, adding an `OrderNotFound` response type to the consumer as shown eliminates throwing an exception since a missing order isn't really a fault.
+
+```csharp
+public class CheckOrderStatusConsumer :
+ IConsumer
+{
+ public async Task Consume(ConsumeContext context)
+ {
+ var order = await _orderRepository.Get(context.Message.OrderId);
+ if (order == null)
+ await context.RespondAsync(context.Message);
+ else
+ await context.RespondAsync(new
+ {
+ OrderId = order.Id,
+ order.Timestamp,
+ order.StatusCode,
+ order.StatusText
+ });
+ }
+}
+```
+
+The client can now wait for multiple response types (in this case, two) by using a little tuple magic.
+
+```csharp
+var response = await client.GetResponse(new { OrderId = id});
+
+if (response.Is(out Response responseA))
+{
+ // do something with the order
+}
+else if (response.Is(out Response responseB))
+{
+ // the order was not found
+}
+```
+
+This cleans up the processing, an eliminates the need to catch a `RequestFaultException`.
+
+It's also possible to use some of the switch expressions via deconstruction, but this requires the response variable to be explicitly specified as `Response`.
+
+```csharp
+Response response = await client.GetResponse(new { OrderId = id});
+
+// Using a regular switch statement
+switch (response)
+{
+ case (_, OrderStatusResult a) responseA:
+ // order found
+ break;
+ case (_, OrderNotFound b) responseB:
+ // order not found
+ break;
+}
+
+// Or using a switch expression
+var accepted = response switch
+{
+ (_, OrderStatusResult a) => true,
+ (_, OrderNotFound b) => false,
+ _ => throw new InvalidOperationException()
+};
+```
+
+### Accept Response Types
+
+The request client sets a message header, `MT-Request-AcceptType`, that contains the response types supported by the request client. This allows the request consumer to determine if the client can handle a response type, which can be useful as services evolve and new response types may be added to handle new conditions. For instance, if a consumer adds a new response type, such as `OrderAlreadyShipped`, if the response type isn't supported an exception may be thrown instead.
+
+To see this in code, check out the client code:
+
+```csharp
+var response = await client.GetResponse(new CancelOrder());
+
+if (response.Is(out Response canceled))
+{
+ return Ok();
+}
+else if (response.Is(out Response responseB))
+{
+ return NotFound();
+}
+```
+
+The original consumer, prior to adding the new response type:
+
+```csharp
+public async Task Consume(ConsumeContext context)
+{
+ var order = _repository.Load(context.Message.OrderId);
+ if(order == null)
+ {
+ await context.ResponseAsync(new { context.Message.OrderId });
+ return;
+ }
+
+ order.Cancel();
+
+ await context.RespondAsync(new { context.Message.OrderId });
+}
+```
+
+Now, the new consumer that checks if the order has already shipped:
+
+```csharp
+public async Task Consume(ConsumeContext context)
+{
+ var order = _repository.Load(context.Message.OrderId);
+ if(order == null)
+ {
+ await context.ResponseAsync(new { context.Message.OrderId });
+ return;
+ }
+
+ if(order.HasShipped)
+ {
+ if (context.IsResponseAccepted())
+ {
+ await context.RespondAsync(new { context.Message.OrderId, order.ShipDate });
+ return;
+ }
+ else
+ throw new InvalidOperationException("The order has already shipped"); // to throw a RequestFaultException in the client
+ }
+
+ order.Cancel();
+
+ await context.RespondAsync(new { context.Message.OrderId });
+}
+```
+
+This way, the consumer can check the request client response types and act accordingly.
+
+::alert{type="success"}
+For backwards compatibility, if the new `MT-Request-AcceptType` header is not found, `IsResponseAccepted` will return true for all message types.
+::
+
+### Concurrent Requests
+
+If there were multiple requests to be performed, it is easy to wait on all results at the same time, benefiting from the concurrent operation.
+
+```csharp
+public class RequestController :
+ Controller
+{
+ IRequestClient _clientA;
+ IRequestClient _clientB;
+
+ public RequestController(IRequestClient clientA, IRequestClient clientB)
+ {
+ _clientA = clientA;
+ _clientB = clientB;
+ }
+
+ public async Task Get()
+ {
+ var resultA = _clientA.GetResponse(new RequestA());
+ var resultB = _clientB.GetResponse(new RequestB());
+
+ await Task.WhenAll(resultA, resultB);
+
+ var a = await resultA;
+ var b = await resultB;
+
+ var model = new Model(a.Message, b.Message);
+
+ return View(model);
+ }
+}
+```
+
+The power of concurrency, for the win!
+
+### Request Handle
+
+Client factories or the request client can also be used to create a request instead of calling `GetResponse`. This is an uncommon scenario, but is available as an option and may make sense depending on the situation. If a request is created (which returns a `RequestHandle`), the request handle must be disposed after the request completes.
+
+> Using `Create` returns a request handle, which can be used to set headers and other attributes of the request before it is sent.
+
+```csharp
+public interface IRequestClient
+ where TRequest : class
+{
+ RequestHandle Create(TRequest request, CancellationToken cancellationToken, RequestTimeout timeout);
+}
+```
+
+> For `RequestTimeout` three options are available, `None`, `Default`, and a factory with `RequestTimeout.After`. `None` would never be recommended since it would essentially wait forever for a response. There is always a relevant timeout, or you're using the wrong pattern.
+
+## Request Client Factory
+
+> The internals are documented for understanding, but what follows is optional reading. The above container-based configuration handles all the details to ensure the proper context is used.
+
+The request client is composed of two parts, a client factory and a request client. There are two client factories, the scoped client factory, and the bus client factory.
+
+### IScopedClientFactory
+
+Using `IRequestClient` requires a container scope, and the request client for a request message type is resolved from container scope using a scoped client factory. As an alternative to specifying `IRequestClient` as a constructor dependency, the scoped client factory can be used instead of create a request client directly. This can be useful when the destination address may change based on context, such as a _TenantId_.
+
+```csharp
+public interface IScopedClientFactory
+{
+ IRequestClient CreateRequestClient(RequestTimeout timeout = default)
+ where T : class;
+
+ IRequestClient CreateRequestClient(Uri destinationAddress, RequestTimeout timeout = default)
+ where T : class;
+}
+```
+
+An example showing how to use `IScopedClientFactory` is shown below.
+
+```csharp
+[HttpGet]
+public async Task HandleGet(string tenantId, int id, [FromServices] IScopedClientFactory clientFactory)
+{
+ var serviceAddress = new Uri($"exchange:check-order-status-{tenantId}");
+
+ var client = clientFactory.CreateRequestClient(serviceAddress);
+
+ var response = await client.GetResponse(new { OrderId = id});
+
+ return Ok();
+}
+```
+
+### IClientFactory
+
+If there is no container scope available, and one cannot be created, the root client factory can be used instead. *Note that non-scoped interfaces are not compatible with scoped publish or send filters*.
+
+```csharp
+public interface IClientFactory
+{
+ IRequestClient CreateRequestClient(ConsumeContext context, Uri destinationAddress, RequestTimeout timeout);
+
+ IRequestClient CreateRequestClient(Uri destinationAddress, RequestTimeout timeout);
+}
+```
+
+An example showing how to use `IClientFactory` is shown below.
+
+```csharp
+public async Task WorkerMethod(IServiceProvider provider)
+{
+ var clientFactory = provider.GetRequiredService();
+
+ var serviceAddress = new Uri("exchange:check-order-status");
+
+ var client = clientFactory.CreateRequestClient(serviceAddress);
+
+ var response = await client.GetResponse(new { OrderId = id});
+}
+```
+
diff --git a/doc/content/3.documentation/1.concepts/7.routing-slips.md b/doc/content/3.documentation/1.concepts/7.routing-slips.md
new file mode 100644
index 00000000000..e25b4026bdc
--- /dev/null
+++ b/doc/content/3.documentation/1.concepts/7.routing-slips.md
@@ -0,0 +1,422 @@
+# Routing Slips
+
+Developing applications with a distributed, message-based architecture adds complexity to handling transactions, especially when all steps must either succeed together or fail completely. In traditional applications using an ACID database, transactions are managed through SQL, where partial operations are rolled back if the transaction fails. However, this approach doesn’t scale well when the transaction spans multiple services or databases. In modern microservices architectures, the reliance on a single ACID database has become increasingly rare.
+
+MassTransit Routing Slips address this challenge by enabling distributed transactions with fault compensation, designed to scale across a network of services. It provides functionality previously handled by database transactions, but adapted for distributed systems. Routing Slips also integrate seamlessly with saga state machines, which add capabilities for transaction monitoring and recoverability.
+
+MassTransit implements the [Routing Slip pattern](https://www.enterpriseintegrationpatterns.com/patterns/messaging/RoutingTable.html), leveraging durable messaging transports and the advanced saga features of MassTransit. Routing slips simplify the coordination of distributed transactions. When combined with a saga state machine, routing slips create a robust, recoverable, and maintainable approach to message processing across multiple services.
+
+Beyond basic routing slip functionality, MassTransit also supports [compensations][1], allowing activities to store execution data so that reversible operations can be undone. Compensation can be achieved either through traditional rollbacks or by performing offsetting operations. For instance, an activity that reserves a seat for a customer could release that reservation if compensation is triggered.
+
+## Activities
+
+In a MassTransit routing slip, an *Activity* refers to a processing step that can be added to a routing slip.
+
+### Compensating
+
+To create an activity, create a class that implements the `IActivity` interface for activities that support compensation or `IExecuteActivity` for those that don't need compensation.
+
+```csharp
+public class DownloadImageActivity :
+ IActivity
+{
+ Task Execute(ExecuteContext context);
+ Task Compensate(CompensateContext context);
+}
+```
+
+The `IActivity` interface has two generic arguments. The first specifies the activity’s argument type and the second specifies the activity’s log type. In the example above, `DownloadImageArguments` is the argument type and `DownloadImageLog` is the log type. The type parameters may be an interface, class or record type. Where the type is a class or a record, the proper accessors should be specified (i.e. `{ get; set; }` or `{ get; init; }`).
+
+### Non-Compensating
+
+```csharp
+public class DownloadImageActivity :
+ IExecuteActivity
+{
+ Task Execute(ExecuteContext context);
+}
+```
+
+::alert{type="info"}
+An *Execute Activity* is an activity that only executes and does not support compensation. As such, the declaration of a log type is not required.
+::
+
+## Implementation
+
+| Verb | Description |
+|:-----------|:----------------------------------------------------------------------------------------------------|
+| Execute | The primary action that the activity performs as part of the workflow. |
+| Compensate | The action the activity must take to undo or reverse its effects if any subsequent activities fail. |
+
+### Execute
+
+Both `IActivity`and `IExecuteActivity` require you to implement the `Execute` method. _Execute_ is called while the routing slip is executing activities.
+
+When *Execute* is called, the `ExecuteContext` argument contains the activity arguments, the routing slip's *TrackingNumber*, and methods to complete or fault the activity. The actual routing slip message, as well as any details of the underlying infrastructure, are excluded to prevent coupling between the activity and the implementation. An example *Execute* method is shown below.
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ DownloadImageArguments args = execution.Arguments;
+ string imageSavePath = Path.Combine(args.WorkPath,
+ execution.TrackingNumber.ToString());
+
+ await _httpClient.GetAndSave(args.ImageUri, imageSavePath);
+
+ return execution.Completed(new {ImageSavePath = imageSavePath});
+}
+```
+
+## Execution Results
+
+After an activity finishes processing, it returns an *ExecutionResult* to the host. If the activity completes successfully, it can choose to store compensation data in an activity log, which is passed to the *Completed* method on the `ExecuteContext` argument. If no compensation data is needed, the activity log is optional. In addition to compensation data, the activity can also add or modify variables stored in the routing slip for use by subsequent activities.
+
+| Result | Description |
+|:----------|:--------------------------------------------------------------------------------|
+| Complete | Indicates the activity completed successfully. |
+| Fault | Indicates the activity failed. |
+| Terminate | Indicates the routing slip should stop, but the process is considered complete. |
+
+### Completing
+
+#### Complete
+
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ DownloadImageArguments args = execution.Arguments;
+ string imageSavePath = Path.Combine(args.WorkPath,
+ execution.TrackingNumber.ToString());
+
+ await _httpClient.GetAndSave(args.ImageUri, imageSavePath);
+
+ // success with no compensation log
+ return execution.Completed();
+}
+```
+
+#### Complete - With Compensation Log
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ DownloadImageArguments args = execution.Arguments;
+ string imageSavePath = Path.Combine(args.WorkPath,
+ execution.TrackingNumber.ToString());
+
+ await _httpClient.GetAndSave(args.ImageUri, imageSavePath);
+
+ return execution.Completed(new {ImageSavePath = imageSavePath});
+}
+```
+
+In the example above, the activity specifies the *DownloadImageLog* interface and initializes the log using an anonymous object. The object is then passed to the *Completed* method for storage in the routing slip before sending the routing slip to the next activity.
+
+### Revise Itinerary
+
+An activity has complete control over the routing slip and can revise the itinerary to include additional activities.
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ DownloadImageArguments args = execution.Arguments;
+ string imageSavePath = Path.Combine(args.WorkPath,
+ execution.TrackingNumber.ToString());
+
+ await _httpClient.GetAndSave(args.ImageUri, imageSavePath);
+
+ return execution.ReviseItinerary(builder =>
+ {
+ // add activity at the beginning of the current itinerary
+ builder.AddActivity("Deviation", new Uri($"exchange:{optionalAddress}"));
+
+ // maintain the existing activities
+ builder.AddActivitiesFromSourceItinerary();
+
+ // add activity at the end of the current itinerary
+ builder.AddActivity("Deviation", new Uri($"exchange:{optionalAddress}"));
+ });
+}
+```
+
+### Faulting
+
+By default, if an activity throws an exception, it will be _faulted_ and a `RoutingSlipFaulted` event will be published (unless a subscription changes the rules). An activity can also return _Faulted_ rather than throwing an exception.
+
+#### Throwing an Exception
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ DownloadImageArguments args = execution.Arguments;
+ string imageSavePath = Path.Combine(args.WorkPath,
+ execution.TrackingNumber.ToString());
+
+ await _httpClient.GetAndSave(args.ImageUri, imageSavePath);
+
+ // will throw an exception
+ var result = 100 / 0;
+
+ return execution.Completed();
+}
+```
+
+In the example above, the activity will throw an exception which will result in a `Fault` which will include data about the exception.
+
+#### Explicitly
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ DownloadImageArguments args = execution.Arguments;
+ string imageSavePath = Path.Combine(args.WorkPath,
+ execution.TrackingNumber.ToString());
+
+ await _httpClient.GetAndSave(args.ImageUri, imageSavePath);
+
+ return execution.Faulted();
+}
+```
+
+In the example above, the activity can look at the data and then explicitly return a `Faulted` result.
+
+### Terminating
+
+In some situations, it may make sense to terminate the routing slip without executing any of the subsequent activities in the itinerary. This might be due to a business rule, in which the routing slip shouldn't be faulted, but needs to end immediately.
+
+To terminate a routing slip, call _Terminate_ as shown.
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ // regular termination
+ return execution.Terminate();
+}
+```
+
+An optional reason can also be specified.
+
+```csharp
+async Task Execute(ExecuteContext execution)
+{
+ // terminate and include additional variables in the event
+ return execution.Terminate(new { Reason = "Not a good time, dude."});
+}
+```
+
+### Compensating
+
+Only `IActivity` requires you to implement the `Compensate` method. _Compensate_ is called when a subsequent activity has faulted so that the activity can undo or reverse its effects.
+
+When an activity fails, the *Compensate* method is called for previously executed activities in the routing slip that stored compensation data. If an activity does not store any compensation data, the *Compensate* method is never called.
+
+```csharp
+return context.Completed(new LogModel {
+ SomeData = "abc"
+})
+```
+
+The compensation method for the example above is shown below.
+
+```csharp
+Task Compensate(CompensateContext compensation)
+{
+ DownloadImageLog log = compensation.Log;
+ File.Delete(log.ImageSavePath);
+
+ return compensation.Compensated();
+}
+```
+
+Using the activity log data, the activity compensates by removing the downloaded image from the work directory. Once the activity has successfully compensated for the previous execution, it returns a *CompensationResult* by calling the *Compensated* method. If the compensation cannot be completed (due to logic issues or exceptions) and this results in a failure, the *Failed* method should be used, optionally providing an *Exception*.
+
+## Building a Routing Slip
+
+A routing slip defines a sequence of processing steps, called *activities*, that are combined into a single itinerary. As each activity finishes, the routing slip is forwarded to the next activity in the itinerary. When all activities are completed, the routing slip is finalized, marking the transaction as complete.
+
+One key advantage of using a routing slip is its flexibility—activities can vary for each transaction. Depending on factors like payment methods, billing/shipping address, or customer preferences, the routing slip builder can dynamically add activities. This is in contrast to the more rigid, predefined behavior of a state machine or sequential workflow, which are statically defined through code, a DSL, or frameworks like Windows Workflow.
+
+### Routing Slip Structure
+
+A routing slip contains an itinerary, variables, and activity/compensation logs. It is defined by a message contract, which the underlying Courier components use to execute and compensate the transaction. The routing slip contract includes:
+
+- A unique tracking number for each routing slip
+- An itinerary, which is an ordered list of activities
+- An activity log, recording an ordered list of previously executed activities
+- A compensation log, listing previously executed activities that can be compensated if the routing slip faults
+- A collection of variables, which can be mapped to activity arguments
+- A collection of subscriptions for notifying consumers of routing slip events
+- A collection of exceptions that may have occurred during routing slip execution
+
+### Routing Slip Builder
+
+Instead of directly implementing the *RoutingSlip* message type, developers are encouraged to use a *RoutingSlipBuilder* to construct the routing slip. The *RoutingSlipBuilder* simplifies the process by providing methods to add activities (and their arguments), activity logs, and variables to the routing slip. For example, to create a routing slip with two activities and an additional variable, a developer might write:
+
+
+```csharp
+var builder = new RoutingSlipBuilder(NewId.NextGuid());
+builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"),
+ new
+ {
+ ImageUri = new Uri("http://images.google.com/someImage.jpg")
+ });
+builder.AddActivity("FilterImage", new Uri("rabbitmq://localhost/execute_filterimage"));
+builder.AddVariable("WorkPath", @"\dfs\work");
+
+var routingSlip = builder.Build();
+```
+
+Each activity requires a name for display purposes and a URI specifying the execution address. The execution address is where the routing slip should be sent to execute the activity. For each activity, arguments can be specified that are stored and presented to the activity via the activity arguments interface type specify by the first argument of the *IActivity* interface. The activities added to the routing slip are combined into an *Itinerary*, which is the list of activities to be executed, and stored in the routing slip.
+
+> Managing the inventory of available activities, as well as their names and execution addresses, is the responsibility of the application and is not part of the MassTransit Courier. Since activities are application specific, and the business logic to determine which activities to execute and in what order is part of the application domain, the details are left to the application developer.
+
+### Activity Arguments
+
+Each activity declares an activity argument type, which must be an interface. When the routing slip is received by an activity host, the argument type is used to read data from the routing slip and deliver it to the activity.
+
+The argument properties are mapped, by name, to the argument type from the routing slip using:
+
+- Explicitly declared arguments, added to the itinerary with the activity
+- Implicitly mapped arguments, added as variables to the routing slip
+
+To specify an explicit activity argument, specify the argument value while adding the activity using the routing slip builder.
+
+```csharp
+var builder = new RoutingSlipBuilder(NewId.NextGuid());
+builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"), new
+ {
+ ImageUri = new Uri("http://images.google.com/someImage.jpg")
+ });
+```
+
+To specify an implicit activity argument, add a variable to the routing slip with the same name/type as the activity argument.
+
+```csharp
+var builder = new RoutingSlipBuilder(NewId.NextGuid());
+builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"));
+builder.AddVariable("ImageUri", "http://images.google.com/someImage.jpg");
+```
+
+If an activity argument is not specified when the routing slip is created, it may be added by an activity that executes prior to the activity that requires the argument. For instance, if the _DownloadImage_ activity stored the image in a local cache, that address could be added and used by another activity to access the cached image.
+
+First, the routing slip would be built without the argument value.
+
+```csharp
+var builder = new RoutingSlipBuilder(NewId.NextGuid());
+builder.AddActivity("DownloadImage", new Uri("rabbitmq://localhost/execute_downloadimage"));
+builder.AddActivity("ProcessImage", new Uri("rabbitmq://localhost/execute_processimage"));
+builder.AddVariable("ImageUri", "http://images.google.com/someImage.jpg");
+```
+
+Then, the first activity would add the variable to the routing slip on completion.
+
+```csharp
+async Task Execute(ExecuteContext context)
+{
+ ...
+ return context.CompletedWithVariables(new { ImagePath = ...});
+}
+```
+
+The process image activity would then use that variable as an argument value.
+
+```csharp
+async Task Execute(ExecuteContext context)
+{
+ var path = context.Arguments.ImagePath;
+}
+```
+
+### Executing
+
+Once built, the routing slip is executed, which sends it to the first activity’s execute URI. To make it easy and to ensure that source information is included, an extension method on *IBus* is available, the usage of which is shown below.
+
+```csharp
+await bus.Execute(routingSlip);
+```
+
+It should be pointed out that if the address for the first activity is invalid or cannot be reached, an exception will be thrown by the *Execute* method.
+
+
+## Routing Slip Events
+
+During routing slip execution, events are published when the routing slip completes or faults. Every event message includes the *TrackingNumber* as well as a *Timestamp* (in UTC, of course) indicating when the event occurred:
+
+ * RoutingSlipCompleted
+ * RoutingSlipFaulted
+ * RoutingSlipCompensationFailed
+
+Additional events are published for each activity, including:
+
+ * RoutingSlipActivityCompleted
+ * RoutingSlipActivityFaulted
+ * RoutingSlipActivityCompensated
+ * RoutingSlipActivityCompensationFailed
+
+By observing these events, an application can monitor and track the state of a routing slip. To maintain the current state, an Automatonymous state machine could be created. To maintain history, events could be stored in a database and then queried using the *TrackingNumber* of the routing slip.
+
+### Subscriptions
+
+By default, routing slip events are published -- which means that any subscribed consumers will receive the events. While this is useful getting started, it can quickly get out of control as applications grow and multiple unrelated routing slips are used. To handle this, subscriptions were added (yes, added, because they weren't though of until we experienced this ourselves).
+
+Subscriptions are added to the routing slip at the time it is built using the `RoutingSlipBuilder`.
+
+```csharp
+builder.AddSubscription(new Uri("rabbitmq://localhost/log-events"),
+ RoutingSlipEvents.All);
+```
+
+This subscription would send all routing slip events to the specified endpoint. If the application only wanted specified events, the events can be selected by specifying the enumeration values for those events. For example, to only get the `RoutingSlipCompleted` and `RoutingSlipFaulted` events, the following code would be used.
+
+```csharp
+builder.AddSubscription(new Uri("rabbitmq://localhost/log-events"),
+ RoutingSlipEvents.Completed | RoutingSlipEvents.Faulted);
+```
+
+It is also possible to tweak the content of the events to cut down on message size. For instance, by default, the `RoutingSlipCompleted` event includes the variables from the routing slip. If the variables contained a large document, that document would be copied to the event. Eliminating the variables from the event would reduce the message size, thereby reducing the traffic on the message broker. To specify the contents of a routing slip event subscription, an additional argument is specified.
+
+```csharp
+builder.AddSubscription(new Uri("rabbitmq://localhost/log-events"),
+ RoutingSlipEvents.Completed, RoutingSlipEventContents.None);
+```
+
+This would send the `RoutingSlipCompleted` event to the endpoint, without any of the variables be included (only the main properties of the event would be present).
+
+> Once a subscription is added to a routing slip, events are no longer published -- they are only sent to the addresses specified in the subscriptions. However, multiple subscriptions can be specified -- the endpoints just need to be known at the time the routing slip is built.
+
+### Custom
+
+It is also possible to specify a subscription with a custom event, a message that is created by the application developer. This makes it possible to create your own event types and publish them in response to routing slip events occurring. And this includes having the full context of a regular endpoint `Send` so that any headers or context settings can be applied.
+
+To create a custom event subscription, use the overload shown below.
+
+```csharp
+// first, define the event type in your assembly
+public record OrderProcessingCompleted
+{
+ public Guid TrackingNumber { get; init; }
+ public DateTime Timestamp { get; init; }
+
+ public string OrderId { get; init; }
+ public string OrderApproval { get; init; }
+}
+
+// then, add the subscription with the custom properties
+builder.AddSubscription(new Uri("rabbitmq://localhost/order-events"),
+ RoutingSlipEvents.Completed,
+ x => x.Send(new
+ {
+ OrderId = "BFG-9000",
+ OrderApproval = "ComeGetSome"
+ }));
+```
+
+In the message contract above, there are four properties, but only two of them are specified. By default, the base `RoutingSlipCompleted` event is created, and then the content of that event is *merged* into the message created in the subscription. This ensures that the dynamic values, such as the `TrackingNumber` and the `Timestamp`, which are present in the default event, are available in the custom event.
+
+Custom events can also select with contents are merged with the custom event, using an additional method overload.
+
+
+
+[1]: https://learn.microsoft.com/en-us/azure/architecture/patterns/compensating-transaction
+[2]: https://github.com/MassTransit/Automatonymous
diff --git a/doc/content/3.documentation/1.concepts/mediator.md b/doc/content/3.documentation/1.concepts/mediator.md
index e877473eb0a..eda6e15e8f8 100644
--- a/doc/content/3.documentation/1.concepts/mediator.md
+++ b/doc/content/3.documentation/1.concepts/mediator.md
@@ -35,7 +35,8 @@ _Publish_, on the other hand, does not require the message to be consumed and do
### Scoped Mediator
-Main mediator interface `IMediator` is registered as a singleton but there is another scoped version of it `IScopedMediator`. This interface is registered as a part of current IoC scope (`HttpContext` or manually created) and can be used in order to share it for the entire pipeline.
+Main mediator interface `IMediator` is registered as a singleton but there is another scoped version of it `IScopedMediator`. This interface is registered as a part of current IoC scope (`HttpContext` or manually created) and can be used in order to share the scope for the entire pipeline.
+By default with `IMediator`, each consumer has its own scope. By using `IScopedMediator`, the scope is shared between several consumers.
::alert{type="success"}
No additional configuration is required as long as Mediator is configured via `services.AddMediator()`
diff --git a/doc/content/3.documentation/1.concepts/testing.md b/doc/content/3.documentation/1.concepts/testing.md
deleted file mode 100644
index d301ffedafd..00000000000
--- a/doc/content/3.documentation/1.concepts/testing.md
+++ /dev/null
@@ -1,78 +0,0 @@
-# Testing
-
-MassTransit is a framework, and follows the Hollywood principle – don't call us, we'll call you. This inversion of control, combined with asynchronous execution, can complicate unit tests. To make it easy, MassTransit includes test harnesses to create unit tests that run entirely in-memory but behave close to an actual message broker. In fact, the included memory-based messaging fabric was inspired by RabbitMQ exchanges and queues.
-
-Since MassTransit is typically configured using `AddMassTransit`, the preferred testing approach is to use a `ServiceCollection` to configure the test combined with the test harness.
-
-## Consumer
-
-To test a consumer using container-based configuration:
-
-```csharp
-await using var provider = new ServiceCollection()
- .AddMassTransitTestHarness(cfg =>
- {
- cfg.AddConsumer();
- })
- .BuildServiceProvider(true);
-
-var harness = provider.GetRequiredService();
-
-await harness.Start();
-
-var client = harness.GetRequestClient();
-
-await client.GetResponse(new
-{
- OrderId = InVar.Id,
- OrderNumber = "123"
-});
-
-Assert.IsTrue(await harness.Sent.Any());
-
-Assert.IsTrue(await harness.Consumed.Any());
-
-var consumerHarness = harness.GetConsumerHarness();
-
-Assert.That(await consumerHarness.Consumed.Any());
-```
-
-## Saga State Machine
-
-To test a saga state machine using container-based configuration:
-
-```csharp
-await using var provider = new ServiceCollection()
- .AddMassTransitTestHarness(cfg =>
- {
- cfg.AddSagaStateMachine();
- })
- .BuildServiceProvider(true);
-
-var harness = provider.GetRequiredService();
-
-await harness.Start();
-
-var sagaId = Guid.NewGuid();
-var orderNumber = "ORDER123";
-
-await harness.Bus.Publish(new OrderSubmitted
-{
- CorrelationId = sagaId,
- OrderNumber = orderNumber
-});
-
-Assert.That(await harness.Consumed.Any());
-
-var sagaHarness = harness.GetSagaStateMachineHarness();
-
-Assert.That(await sagaHarness.Consumed.Any());
-
-Assert.That(await sagaHarness.Created.Any(x => x.CorrelationId == sagaId));
-
-var instance = sagaHarness.Created.ContainsInState(sagaId, sagaHarness.StateMachine, sagaHarness.StateMachine.Submitted);
-Assert.IsNotNull(instance, "Saga instance not found");
-Assert.That(instance.OrderNumber, Is.EqualTo(orderNumber));
-
-Assert.IsTrue(await harness.Published.Any());
-```
diff --git a/doc/content/3.documentation/2.configuration/0.index.md b/doc/content/3.documentation/2.configuration/0.index.md
new file mode 100755
index 00000000000..bbd7e45b103
--- /dev/null
+++ b/doc/content/3.documentation/2.configuration/0.index.md
@@ -0,0 +1,656 @@
+---
+navigation.title: Overview
+---
+
+# Configuration
+
+MassTransit is usable in most .NET application types. MassTransit is easily configured in ASP.NET Core or .NET Generic Host applications (using .NET 6 or later).
+
+To use MassTransit, add the _MassTransit_ package (from NuGet) and start with the _AddMassTransit_ method shown below.
+
+```csharp
+using MassTransit;
+
+services.AddMassTransit(x =>
+{
+ // A Transport
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ });
+});
+```
+
+In this configuration, the following variables are used:
+
+| Variable | Type | Description |
+|-----------|:----------------------------------|-------------------------------------------------------------------------------------------|
+| `x` | `IBusRegistrationConfigurator` | Configure the bus instance (not transport specific) and the underlying service collection |
+| `context` | `IBusRegistrationContext` | The configured bus context, also implements `IServiceProvider` |
+| `cfg` | `IRabbitMqBusFactoryConfigurator` | Configure the bus specific to the transport (each transport has its own interface type |
+
+:::alert{type="info"}
+The callback passed to the _UsingRabbitMq_ method is invoked after the service collection has been built. Any methods to configure the bus instance (using `x`) should be called outside of this callback.
+:::
+
+Adding MassTransit, as shown above, will configure the service collection with required components, including:
+
+ * Several interfaces (and their implementations, appropriate for the transport specified)
+ * `IBus` (singleton)
+ * `IBusControl` (singleton)
+ * `IReceiveEndpointConnector` (singleton)
+ * `ISendEndpointProvider` (scoped)
+ * `IPublishEndpoint` (scoped)
+ * `IRequestClient` (scoped)
+ * The bus endpoint with the default settings (not started by default)
+ * The _MassTransitHostedService_
+ * Health checks for the bus (or buses) and receive endpoints
+ * Using `ILoggerFactory` for log output
+
+> To configure multiple bus instances in the same service collection, refer to the [MultiBus](/documentation/configuration/multibus) section.
+
+## Host Options
+
+MassTransit adds a hosted service so that the generic host can start and stop the bus (or buses, if multiple bus instances are configured). The host options can be configured via _MassTransitHostOptions_ using the _Options_ pattern as shown below.
+
+```csharp
+services.AddOptions()
+ .Configure(options =>
+ {
+ });
+```
+
+| Option | Description |
+|---------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| WaitUntilStarted | By default, MassTransit connects to the broker asynchronously. When set to _true_, the MassTransit Hosted Service will block startup until the broker connection has been established. |
+| StartTimeout | By default, MassTransit waits infinitely until the broker connection is established. If specified, MassTransit will give up after the timeout has expired. |
+| StopTimeout | MassTransit waits infinitely for the bus to stop, including any active message consumers. If specified, MassTransit will force the bus to stop after the timeout has expired. |
+| ConsumerStopTimeout | If specified, the `ConsumeContext.CancellationToken` will be canceled after the specified timeout when the bus is stopping. This allows long-running consumers to observe the cancellation token and react accordingly. Must be <= the `StopTimeout` |
+
+::callout{type="info"}
+#summary
+The .NET Generic Host has its own internal shutdown timeout.
+#content
+To configure the Generic Host options so that the bus has sufficient time to stop, configure the host options as shown.
+
+```csharp
+services.Configure(
+ options => options.ShutdownTimeout = TimeSpan.FromMinutes(1));
+```
+::
+
+## Transport Options
+
+Each supported transport can be configured via a `.Host()` method or via the .NET [Options Pattern](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-7.0).
+
+- Rabbit MQ: [RabbitMqTransportOptions](/documentation/configuration/transports/rabbitmq#transport-options)
+- Azure Service Bus: [AzureServiceBusTransportOptions](documentation/configuration/transports/azure-service-bus#transport-options)
+- Amazon SQS: [AmazonSqsTransportOptions](/documentation/configuration/transports/amazon-sqs#transport-options)
+
+## Consumer Registration
+
+To consume messages, one or more consumers must be added and receive endpoints configured for the added consumers. MassTransit connects each receive endpoint to a queue on the message broker.
+
+To add a consumer and automatically configure a receive endpoint for the consumer, call one of the [_AddConsumer_](/documentation/configuration/bus/consumers) methods and call [_ConfigureEndpoints_](documentation/configuration#configure-endpoints) as shown below.
+
+```csharp
+services.AddMassTransit(x =>
+{
+ x.AddConsumer();
+
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ cfg.ConfigureEndpoints(context);
+ });
+});
+```
+
+:::alert{type="info"}
+**_ConfigureEndpoints_** should be the last method called after all settings and middleware components have been configured.
+:::
+
+MassTransit will automatically configure a receive endpoint for the _SubmitOrderConsumer_ using the name returned by the configured endpoint name formatter. When the bus is started, the receive endpoint will be started and messages will be delivered from the queue by the transport to an instance of the consumer.
+
+> All consumer types can be added, including consumers, sagas, saga state machines, and routing slip activities. If a job consumer is added, [additional configuration](/documentation/patterns/job-consumers) is required.
+
+::callout{type="info"}
+#summary
+Learn about the default conventions as well as how to tailor the naming style to meet your requirements in this short video:
+#content
+::div
+ :video-player{src="https://www.youtube.com/watch?v=bsUlQ93j2MY"}
+::
+::
+
+To exclude a consumer, saga, or routing slip activity from automatic configuration, use the _ExcludeFromConfigureEndpoints_ extension method when adding the consumer:
+
+```csharp
+x.AddConsumer()
+ .ExcludeFromConfigureEndpoints()
+```
+
+Alternatively, the _ExcludeFromConfigureEndpoints_ attribute may be specified on the consumer.
+
+```csharp
+[ExcludeFromConfigureEndpoints]
+public class SubmitOrderConsumer :
+ IConsumer
+{
+}
+```
+
+## Configure Endpoints
+
+As shown in the example above, using `ConfigureEndpoints` is the preferred approach to configure receive endpoints. By registering consumers, sagas, and routing slip activities along with their optional definitions, MassTransit is able to configure receive endpoints for all registered consumer types. Receive endpoint names are generated using an [endpoint name formatter](#endpoint-name-formatters) (unless otherwise specified in a definition), and each receive endpoint is configured.
+
+As receive endpoints are configured, one or more consumer types are configured on each receive endpoint. If multiple consumer types share the same endpoint name, those consumer types will be configured on the same receive endpoint. For each consumer type, its respective consumer, saga, or activity definition will be applied to the receive endpoint.
+
+::alert{type="warning"}
+If multiple consumer types share the same receive endpoint, and more than one of those consumer types have a matching definition that specifies the same middleware component, **multiple** filters may be configured! This may lead to unpredictable results, so caution is advised when configuring multiple consumer types on the same receive endpoint.
+::
+
+#### Configure Endpoints Callback
+
+To apply receive endpoint settings or configure middleware for all receive endpoints configured by `ConfigureEndpoints`, a callback can be added.
+
+```csharp
+x.AddConfigureEndpointsCallback((name, cfg) =>
+{
+ cfg.UseMessageRetry(r => r.Immediate(2));
+});
+```
+
+When `ConfigureEndpoints` is called, any registered callbacks will be called for every recieve endpoint endpoint. Each callback will only be called _once_ per receive endpoint.
+
+To conditionally apply transport-specific settings, the `cfg` parameter can be pattern-matched to the transport type as shown below.
+
+```csharp
+x.AddConfigureEndpointsCallback((name, cfg) =>
+{
+ if (cfg is IRabbitMqReceiveEndpointConfigurator rmq)
+ rmq.SetQuorumQueue(3);
+
+ cfg.UseMessageRetry(r => r.Immediate(2));
+});
+```
+
+## Endpoint Strategies
+
+Deciding how to configure receive endpoints in your application can be easy or hard, depending upon how much energy you want to spend being concerned with things that usually don't matter. However, there are nuances to the following approaches that should be considered.
+
+#### One Consumer for Each Queue
+
+Creates a queue for each registered consumer, saga, and routing slip activity. Separate queues are created for execute and compensate if compensation is supported by the activity.
+
+::alert{type="info"}
+This is the preferred approach since it ensures that every consumer can be configured independently, including retries, delivery, and the outbox. It also ensures that messages for a consumer are not stuck behind other messages for other consumers sharing the same queue.
+::
+
+#### Multiple Consumers on a Single Queue
+
+Configuring multiple consumers, while fully supported by MassTransit, may make sense in certain circumstances, however, proceed with caution as there are limitations to this approach.
+
+The recommendation here is to configure multiple consumers on a single queue only when those consumers are closely related in terms of business function and each consumer consumes distinct message types. An example might be consumers that each create, update, or delete an entity when the dependencies of those operations are different – create and update may depend upon a validation component, while delete may not share that dependency.
+
+##### Consume Multiple Message Types
+
+In situations where it is preferable to consume multiple message types from a single queue, create a consumer that consumes multiple message types by adding more IConsumer interface implementations to the consumer class.
+
+```csharp
+public class AddressConsumer :
+ IConsumer,
+ IConsumer
+{
+}
+```
+
+Sagas follow this approach, creating a single queue for each saga and configuring the broker to route message types consumed by the saga that are published to topic/exchanges to the saga’s queue.
+
+#### All Consumers on a Single Queue
+
+This is never a good idea and is highly discouraged. While it is supported by MassTransit, it’s unlikely to be operationally sustainable.
+
+Routing slip activities must not be configured on a single queue as they will not work properly.
+
+
+
+
+## Endpoint Name Formatters
+
+_ConfigureEndpoints_ uses an `IEndpointNameFormatter` to format the queue names for all supported consumer types. The default endpoint name formatter returns _PascalCase_ class names without the namespace. There are several built-in endpoint name formatters included. For the _SubmitOrderConsumer_, the receive endpoint names would be formatted as shown below. Note that class suffixes such as _Consumer_, _Saga_, and _Activity_ are trimmed from the endpoint name by default.
+
+| Format | Configuration | Name |
+|:-----------|:------------------------------------|:---------------|
+| Default | `SetDefaultEndpointNameFormatter` | `SubmitOrder` |
+| Snake Case | `SetSnakeCaseEndpointNameFormatter` | `submit_order` |
+| Kebab Case | `SetKebabCaseEndpointNameFormatter` | `submit-order` |
+
+The endpoint name formatters can also be customized by constructing a new instance and configuring MassTransit to use it.
+
+```csharp
+x.SetEndpointNameFormatter(new KebabCaseEndpointNameFormatter(prefix: "Dev", includeNamespace: false));
+```
+
+By specifying a prefix, the endpoint name would be `dev-submit-order`. This is useful when sharing a single broker with multiple developers (Amazon SQS is account-wide, for instance).
+
+::callout{type="info"}
+#summary
+When using MultiBus with different endpoint name formatters for each bus...
+#content
+Specify the endpoint name formatter when calling `ConfigureEndpoints` as shown.
+```csharp
+cfg.ConfigureEndpoints(context, new KebabCaseEndpointNameFormatter(prefix: "Mobile", includeNamespace: false));
+```
+::
+
+## Receive Endpoints
+
+The previous examples use conventions to configure receive endpoints. Alternatively, receive endpoints can be explicitly configured.
+
+> When configuring endpoints manually, _ConfigureEndpoints_ should be excluded or be called **after** any explicitly configured receive endpoints.
+
+To explicitly configure endpoints, use the _ConfigureConsumer_ or _ConfigureConsumers_ method.
+
+```csharp
+services.AddMassTransit(x =>
+{
+ x.AddConsumer();
+
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ cfg.ReceiveEndpoint("order-service", e =>
+ {
+ e.ConfigureConsumer(context);
+ });
+ });
+});
+```
+
+Receive endpoints have transport-independent settings that can be configured.
+
+| Name | Description | Default |
+|:-------------------------|:--------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------------|
+| PrefetchCount | Number of unacknowledged messages delivered by the broker | max(CPU Count x 2,16) |
+| ConcurrentMessageLimit | Number of concurrent messages delivered to consumers | (none, uses PrefetchCount) |
+| ConfigureConsumeTopology | Create exchanges/topics on the broker and bind them to the receive endpoint | true |
+| ConfigureMessageTopology | Create exchanges/topics on the broker and bind them to the receive endpoint for a specific message type | true |
+| PublishFaults | Publish `Fault` events when consumers fault | true |
+| DefaultContentType | The default content type for received messages | See [serialization](configuration/integrations/serialization#serializers) |
+| SerializerContentType | The default content type for sending/publishing messages | See [serialization](configuration/integrations/serialization#serializers) |
+
+> The _PrefetchCount_, _ConcurrentMessageLimit_, and serialization settings can be specified at the bus level and will be applied to all receive endpoints.
+
+In the following example, the _PrefetchCount_ is set to 32 and the _ConcurrentMessageLimit_ is set to 28.
+
+```csharp
+services.AddMassTransit(x =>
+{
+ x.AddConsumer();
+
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ cfg.PrefetchCount = 32; // applies to all receive endpoints
+
+ cfg.ReceiveEndpoint("order-service", e =>
+ {
+ e.ConcurrentMessageLimit = 28; // only applies to this endpoint
+ e.ConfigureConsumer(context);
+ });
+ });
+});
+```
+
+> When using _ConfigureConsumer_ with a consumer that has a definition, the _EndpointName_, _PrefetchCount_, and _Temporary_ properties of the consumer definition are not used.
+
+### Temporary Endpoints
+
+Some consumers only need to receive messages while connected, and any messages published while disconnected should be discarded. This can be achieved by using a TemporaryEndpointDefinition to configure the receive endpoint.
+
+```csharp
+services.AddMassTransit(x =>
+{
+ x.AddConsumer();
+
+ x.UsingInMemory((context, cfg) =>
+ {
+ cfg.ReceiveEndpoint(new TemporaryEndpointDefinition(), e =>
+ {
+ e.ConfigureConsumer(context);
+ });
+
+ cfg.ConfigureEndpoints(context);
+ });
+});
+```
+
+### Consumer Definition
+
+A consumer definition is used to configure the receive endpoint and pipeline behavior for the consumer. When scanning assemblies or namespaces for consumers, consumer definitions are also found and added to the container. The _SubmitOrderConsumer_ and matching definition are shown below.
+
+```csharp
+class SubmitOrderConsumer :
+ IConsumer
+{
+ readonly ILogger _logger;
+
+ public SubmitOrderConsumer(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async Task Consume(ConsumeContext context)
+ {
+ _logger.LogInformation("Order Submitted: {OrderId}", context.Message.OrderId);
+
+ await context.Publish(new
+ {
+ context.Message.OrderId
+ });
+ }
+}
+
+class SubmitOrderConsumerDefinition :
+ ConsumerDefinition
+{
+ public SubmitOrderConsumerDefinition()
+ {
+ // override the default endpoint name
+ EndpointName = "order-service";
+
+ // limit the number of messages consumed concurrently
+ // this applies to the consumer only, not the endpoint
+ ConcurrentMessageLimit = 8;
+ }
+
+ protected override void ConfigureConsumer(IReceiveEndpointConfigurator endpointConfigurator,
+ IConsumerConfigurator consumerConfigurator, IRegistrationContext context)
+ {
+ // configure message retry with millisecond intervals
+ endpointConfigurator.UseMessageRetry(r => r.Intervals(100,200,500,800,1000));
+
+ // use the outbox to prevent duplicate events from being published
+ endpointConfigurator.UseInMemoryOutbox(context);
+ }
+}
+```
+
+### Endpoint Configuration
+
+To configure the endpoint for a consumer registration, or override the endpoint configuration in the definition, the `Endpoint` method can be added to the consumer registration. This will create an endpoint definition for the consumer, and register it in the container. This method is available on consumer and saga registrations, with separate execute and compensate endpoint methods for activities.
+
+```csharp
+services.AddMassTransit(x =>
+{
+ x.AddConsumer()
+ .Endpoint(e =>
+ {
+ // override the default endpoint name
+ e.Name = "order-service-extreme";
+
+ // more options shown below
+ });
+
+ x.UsingRabbitMq((context, cfg) => cfg.ConfigureEndpoints(context));
+});
+```
+
+The configurable settings for an endpoint include:
+
+| Property | Type | Description |
+|:-------------------------------|:---------|:-------------------------------------------------------------------------------------------------------|
+| `Name` | `string` | The receive endpoint (queue) name |
+| `InstanceId` | `string` | If specified, should be unique for each bus instance To enable fan-out (instead of load balancing) |
+| `Temporary` | `bool` | If true, the endpoint will be automatically removed after the bus has stopped. |
+| `PrefetchCount` | `int` | Number of unacknowledged messages delivered by the broker |
+| `ConcurrentMessageLimit` | `int?` | Number of concurrent messages delivered to consumers |
+| `ConfigureConsumeTopology` | `bool` | Create exchanges/topics on the broker and bind them to the receive endpoint |
+| `AddConfigureEndpointCallback` | delegate | (Added in v8.3.0) Adds a callback method that will be invoked when the receive endpoint is configured. |
+
+#### Endpoint Naming
+
+When the endpoint is configured after the _AddConsumer_ method, the configuration then overrides the endpoint configuration in the consumer definition. However, it cannot override the `EndpointName` if it is specified in the constructor. The order of precedence for endpoint naming is explained below.
+
+1. Specifying `EndpointName = "submit-order-extreme"` in the constructor which cannot be overridden
+
+ ```csharp
+ x.AddConsumer()
+
+ public SubmitOrderConsumerDefinition()
+ {
+ EndpointName = "submit-order-extreme";
+ }
+ ```
+
+2. Specifying `.Endpoint(x => x.Name = "submit-order-extreme")` in the consumer registration, chained to `AddConsumer`
+
+ ```csharp
+ x.AddConsumer()
+ .Endpoint(x => x.Name = "submit-order-extreme");
+
+ public SubmitOrderConsumerDefinition()
+ {
+ Endpoint(x => x.Name = "not used");
+ }
+ ```
+
+3. Specifying `Endpoint(x => x.Name = "submit-order-extreme")` in the constructor, which creates an endpoint definition
+
+ ```csharp
+ x.AddConsumer()
+
+ public SubmitOrderConsumerDefinition()
+ {
+ Endpoint(x => x.Name = "submit-order-extreme");
+ }
+ ```
+
+4. Unspecified, the endpoint name formatter is used (in this case, the endpoint name is `SubmitOrder` using the default formatter)
+
+ ```csharp
+ x.AddConsumer()
+
+ public SubmitOrderConsumerDefinition()
+ {
+ }
+ ```
+
+## Saga Registration
+
+To add a state machine saga, use the _AddSagaStateMachine_ methods. For a consumer saga, use the _AddSaga_ methods.
+
+::alert{type="success"}
+State machine sagas should be added before class-based sagas, and the class-based saga methods should not be used to add state machine sagas. This may be simplified in the future, but for now, be aware of this registration requirement.
+::
+
+```csharp
+services.AddMassTransit(r =>
+{
+ // add a state machine saga, with the in-memory repository
+ r.AddSagaStateMachine()
+ .InMemoryRepository();
+
+ // add a consumer saga with the in-memory repository
+ r.AddSaga()
+ .InMemoryRepository();
+
+ // add a saga by type, without a repository. The repository should be registered
+ // in the container elsewhere
+ r.AddSaga(typeof(OrderSaga));
+
+ // add a state machine saga by type, including a saga definition for that saga
+ r.AddSagaStateMachine(typeof(OrderState), typeof(OrderStateDefinition))
+
+ // add all saga state machines by type
+ r.AddSagaStateMachines(Assembly.GetExecutingAssembly());
+
+ // add all sagas in the specified assembly
+ r.AddSagas(Assembly.GetExecutingAssembly());
+
+ // add sagas from the namespace containing the type
+ r.AddSagasFromNamespaceContaining();
+ r.AddSagasFromNamespaceContaining(typeof(OrderSaga));
+});
+```
+
+To add a saga registration and configure the consumer endpoint in the same expression, a definition can automatically be created.
+
+```csharp
+services.AddMassTransit(r =>
+{
+ r.AddSagaStateMachine()
+ .NHibernateRepository()
+ .Endpoint(e =>
+ {
+ e.Name = "order-state";
+ e.ConcurrentMessageLimit = 8;
+ });
+});
+```
+
+Supported saga persistence storage engines are documented in the [saga documentation](/documentation/patterns/saga/) section.
+
+
+```csharp
+services.AddMassTransit(x =>
+{
+ x.AddConsumer();
+
+ x.SetKebabCaseEndpointNameFormatter();
+
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ cfg.ConfigureEndpoints(context);
+ });
+});
+```
+
+And the consumer:
+
+```csharp
+class ValueEnteredEventConsumer :
+ IConsumer
+{
+ ILogger _logger;
+
+ public ValueEnteredEventConsumer(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async Task Consume(ConsumeContext context)
+ {
+ _logger.LogInformation("Value: {Value}", context.Message.Value);
+ }
+}
+```
+
+An ASP.NET Core application can also configure receive endpoints. The consumer, along with the receive endpoint, is configured within the _AddMassTransit_ configuration. Separate registration of the consumer is not required (and discouraged), however, any consumer dependencies should be added to the container separately. Consumers are registered as scoped, and dependencies should be registered as scoped when possible, unless they are singletons.
+
+```csharp
+services.AddMassTransit(x =>
+{
+ x.AddConsumer();
+
+ x.UsingRabbitMq((context, cfg) =>
+ {
+ cfg.ReceiveEndpoint("event-listener", e =>
+ {
+ e.ConfigureConsumer(context);
+ });
+ });
+});
+```
+
+```csharp
+class EventConsumer :
+ IConsumer
+{
+ ILogger _logger;
+
+ public EventConsumer(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ public async Task Consume(ConsumeContext context)
+ {
+ _logger.LogInformation("Value: {Value}", context.Message.Value);
+ }
+}
+```
+
+## Health Checks
+
+The _AddMassTransit_ method adds an `IHealthCheck` to the service collection that you can use to monitor your health. The health check is added with the tags `ready` and `masstransit`.
+
+
+To configure health checks, map the ready and live endpoints in your ASP.NET application.
+
+```csharp
+app.MapHealthChecks("/health/ready", new HealthCheckOptions()
+{
+ Predicate = (check) => check.Tags.Contains("ready"),
+});
+
+app.MapHealthChecks("/health/live", new HealthCheckOptions());
+```
+
+**Example Output**
+
+```json
+{
+ "status": "Healthy",
+ "totalDuration": "00:00:00.2134026",
+ "entries": {
+ "masstransit-bus": {
+ "data": {
+ "Endpoints": {
+ "rabbitmq://localhost/dev-local/SubmitOrder": {
+ "status": "Healthy",
+ "description": "ready"
+ }
+ }
+ },
+ "description": "Ready",
+ "duration": "00:00:00.1853530",
+ "status": "Healthy",
+ "tags": [
+ "ready",
+ "masstransit"
+ ]
+ }
+ }
+}
+```
+
+- When everything works correctly, MassTransit will report `Healthy`.
+- If any problems occur on application startup, MassTransit will report `Unhealthy`. This can cause an orcestrator to restart your application.
+- If any problems occur while the application is working (for example, application loses connection to broker), MassTransit will report `Degraded`.
+
+### Health Check Options
+
+Health Checks can be further configured using _ConfigureHealthCheckOptions_:
+
+```csharp
+builder.Services.AddMassTransit(bus =>
+{
+ bus.ConfigureHealthCheckOptions(options =>
+ {
+ options.Name = "masstransit";
+ options.MinimalFailureStatus = HealthStatus.Unhealthy;
+ options.Tags.Add("health");
+ });
+
+}
+```
+
+| Setting | Description | Default value |
+|:---------------------|:-------------------------------------------------------------------------------|:-----------------------|
+| Name | Set the health check name, overrides the default bus type name. | Bus name. |
+| MinimalFailureStatus | The minimal `HealthStatus` that will be reported when the health check fails. | `Unhealthy` |
+| Tags | A list of tags that can be used to filter sets of health checks. | "ready", "masstransit" |
+
+By default MassTransit reports all three statuses depending on application state.
+If `MinimalFailureStatus` is set to `Healthy`, MassTransit will log any issues, but the health check will always report `Healthy`.
+If `MinimalFailureStatus` is set to `Degraded`, MassTransit will report `Degraded` if any issues occur, but never report `Unhealthy`.
+
+Tags inside options will override default tags. You will need to add `ready` and `masstransit` tags manually if you want to keep them.
diff --git a/doc/content/3.documentation/2.configuration/1.consumers.md b/doc/content/3.documentation/2.configuration/1.consumers.md
new file mode 100644
index 00000000000..fa10a3e8927
--- /dev/null
+++ b/doc/content/3.documentation/2.configuration/1.consumers.md
@@ -0,0 +1,253 @@
+# Consumers
+
+To understand consumers and how to create one, refer to the [Consumers](/documentation/concepts/consumers) section.
+
+High-level concepts covered in this configuration section include:
+
+
+| Concept | Description |
+|---------------------|------------------------------------------------------------------------------------------------------------------------|
+| Consumer | A class that consumes one or more messages types, one for each implementation of `IConsumer` |
+| Batch Consumer | A class that consumes multiple messages in batches, by implementing `IConsumer>` |
+| Job Consumer | A class that consumes a job message, specified by the `IJobConsumer` interface |
+| Consumer Definition | A class, derived from `ConsumerDefinition` that configures settings and the consumer's receive endpoint |
+| Receive Endpoint | Receives messages from a broker queue and delivers those messages to consumer types configured on the receive endpoint |
+
+Consumers can be added many ways allowing either a simple of fine-grained approach to registration. Consumers are added inside the `AddMassTransit` configuration, but before the transport.
+
+```csharp
+using MassTransit;
+
+services.AddMassTransit(x =>
+{
+ x.AddConsumer();
+
+ x.Using[Transport]((context, cfg) =>
+ {
+ // transport, middleware, other configuration
+
+ cfg.ConfigureEndpoints(context);
+ });
+});
+```
+
+## Adding Consumers
+
+Adds a single consumer, with all defaults
+
+```csharp
+AddConsumer