├── .editorconfig
├── .github
├── FUNDING.yml
├── dependabot.yml
└── workflows
│ ├── build-workflow.yml
│ ├── build.yml
│ └── codeql-analysis.yml
├── .gitignore
├── .vscode
├── launch.json
├── settings.json
└── tasks.json
├── Foundatio.All.code-workspace
├── Foundatio.All.sln
├── Foundatio.sln
├── LICENSE.txt
├── NuGet.config
├── README.md
├── build
├── Foundatio.snk
├── Update-Nito.ps1
├── common.props
└── foundatio-icon.png
├── global.json
├── media
├── colors.png
├── foundatio-dark-bg.svg
├── foundatio-icon.png
├── foundatio-icon.svg
├── foundatio-white.png
├── foundatio.png
└── foundatio.svg
├── samples
├── Foundatio.AppHost
│ ├── Extensions
│ │ ├── ImageTags.cs
│ │ └── RedisExtensions.cs
│ ├── Foundatio.AppHost.csproj
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── appsettings.Development.json
│ └── appsettings.json
└── Foundatio.HostingSample
│ ├── Foundatio.HostingSample.csproj
│ ├── Jobs
│ ├── EveryMinuteJob.cs
│ ├── Sample1Job.cs
│ ├── Sample2Job.cs
│ └── SampleLockJob.cs
│ ├── MyCriticalHealthCheck.cs
│ ├── Program.cs
│ ├── Properties
│ └── launchSettings.json
│ ├── ServiceDefaults.cs
│ ├── Startup
│ ├── MyStartupAction.cs
│ └── OtherStartupAction.cs
│ ├── appsettings.Development.json
│ └── appsettings.json
├── src
├── Directory.Build.props
├── Foundatio.DataProtection
│ ├── Extensions
│ │ └── DataProtectionBuilderExtensions.cs
│ ├── Foundatio.DataProtection.csproj
│ └── FoundatioStorageXmlRepository.cs
├── Foundatio.Extensions.Hosting
│ ├── Cronos
│ │ ├── CalendarHelper.cs
│ │ ├── CronExpression.cs
│ │ ├── CronExpressionFlag.cs
│ │ ├── CronField.cs
│ │ ├── CronFormat.cs
│ │ ├── CronFormatException.cs
│ │ └── TimeZoneHelper.cs
│ ├── Foundatio.Extensions.Hosting.csproj
│ ├── Jobs
│ │ ├── Cron.cs
│ │ ├── DynamicJob.cs
│ │ ├── HostedJobOptions.cs
│ │ ├── HostedJobService.cs
│ │ ├── JobHostExtensions.cs
│ │ ├── JobManager.cs
│ │ ├── JobOptionsBuilder.cs
│ │ ├── ScheduledJobInstance.cs
│ │ ├── ScheduledJobOptions.cs
│ │ ├── ScheduledJobOptionsBuilder.cs
│ │ ├── ScheduledJobRegistration.cs
│ │ ├── ScheduledJobService.cs
│ │ └── ShutdownHostIfNoJobsRunningService.cs
│ └── Startup
│ │ ├── IStartupAction.cs
│ │ ├── RunStartupActionsService.cs
│ │ ├── StartupActionRegistration.cs
│ │ ├── StartupActionsContext.cs
│ │ ├── StartupExtensions.cs
│ │ ├── StartupHealthcheck.cs
│ │ ├── StartupPriorityAttribute.cs
│ │ └── WaitForStartupActionsBeforeServingRequestsMiddleware.cs
├── Foundatio.JsonNet
│ ├── Foundatio.JsonNet.csproj
│ └── JsonNetSerializer.cs
├── Foundatio.MessagePack
│ ├── Foundatio.MessagePack.csproj
│ └── MessagePackSerializer.cs
├── Foundatio.TestHarness
│ ├── Caching
│ │ ├── CacheClientTestsBase.cs
│ │ └── HybridCacheClientTests.cs
│ ├── Extensions
│ │ └── TaskExtensions.cs
│ ├── Foundatio.TestHarness.csproj
│ ├── GlobalSuppressions.cs
│ ├── Jobs
│ │ ├── HelloWorldJob.cs
│ │ ├── JobQueueTestsBase.cs
│ │ ├── SampleQueueJob.cs
│ │ ├── ThrottledJob.cs
│ │ ├── WithDependencyJob.cs
│ │ └── WithLockingJob.cs
│ ├── Locks
│ │ └── LockTestBase.cs
│ ├── Messaging
│ │ ├── MessageBusTestBase.cs
│ │ └── Samples.cs
│ ├── Queue
│ │ ├── QueueTestBase.cs
│ │ └── Samples.cs
│ ├── Serializer
│ │ └── SerializerTestsBase.cs
│ ├── Storage
│ │ └── FileStorageTestsBase.cs
│ └── Utility
│ │ ├── BenchmarkToJson.cs
│ │ ├── Configuration.cs
│ │ ├── InMemoryMetrics.cs
│ │ └── NonSeekableStream.cs
├── Foundatio.Utf8Json
│ ├── Foundatio.Utf8Json.csproj
│ └── Utf8JsonSerializer.cs
├── Foundatio.Xunit
│ ├── Foundatio.Xunit.csproj
│ ├── Logging
│ │ ├── LogEntry.cs
│ │ ├── LoggingExtensions.cs
│ │ ├── TestLogger.cs
│ │ ├── TestLoggerBase.cs
│ │ ├── TestLoggerFixture.cs
│ │ ├── TestLoggerLogger.cs
│ │ ├── TestLoggerOptions.cs
│ │ ├── TestLoggerProvider.cs
│ │ └── TestWithLoggingBase.cs
│ └── Retry
│ │ ├── DelayedMessageBus.cs
│ │ ├── RetryAttribute.cs
│ │ ├── RetryFactDiscoverer.cs
│ │ ├── RetryTestCase.cs
│ │ ├── RetryTheoryAttribute.cs
│ │ ├── RetryTheoryDiscoverer.cs
│ │ └── RetryTheoryTestCase.cs
└── Foundatio
│ ├── Caching
│ ├── CacheValue.cs
│ ├── HybridCacheClient.cs
│ ├── ICacheClient.cs
│ ├── IMemoryCacheClient.cs
│ ├── InMemoryCacheClient.cs
│ ├── InMemoryCacheClientOptions.cs
│ ├── NullCacheClient.cs
│ └── ScopedCacheClient.cs
│ ├── DeepCloner
│ ├── DeepClonerExtensions.cs
│ ├── Helpers
│ │ ├── ClonerToExprGenerator.cs
│ │ ├── DeepCloneState.cs
│ │ ├── DeepClonerCache.cs
│ │ ├── DeepClonerExprGenerator.cs
│ │ ├── DeepClonerGenerator.cs
│ │ ├── DeepClonerSafeTypes.cs
│ │ ├── ReflectionHelper.cs
│ │ ├── ShallowClonerGenerator.cs
│ │ └── ShallowObjectCloner.cs
│ └── LICENSE
│ ├── Extensions
│ ├── CacheClientExtensions.cs
│ ├── CollectionExtensions.cs
│ ├── ConcurrentDictionaryExtensions.cs
│ ├── ConcurrentQueueExtensions.cs
│ ├── DateTimeExtensions.cs
│ ├── DictionaryExtensions.cs
│ ├── EnumExtensions.cs
│ ├── EnumerableExtensions.cs
│ ├── ExceptionExtensions.cs
│ ├── LoggerExtensions.cs
│ ├── NumberExtensions.cs
│ ├── ObjectExtensions.cs
│ ├── StringExtensions.cs
│ ├── TaskExtensions.cs
│ ├── TimespanExtensions.cs
│ └── TypeExtensions.cs
│ ├── Foundatio.csproj
│ ├── Jobs
│ ├── IJob.cs
│ ├── IQueueJob.cs
│ ├── JobAttribute.cs
│ ├── JobBase.cs
│ ├── JobContext.cs
│ ├── JobOptions.cs
│ ├── JobResult.cs
│ ├── JobRunner.cs
│ ├── JobWithLockBase.cs
│ ├── QueueEntryContext.cs
│ ├── QueueJobBase.cs
│ └── WorkItemJob
│ │ ├── WorkItemContext.cs
│ │ ├── WorkItemData.cs
│ │ ├── WorkItemHandlers.cs
│ │ ├── WorkItemJob.cs
│ │ ├── WorkItemQueueExtensions.cs
│ │ └── WorkItemStatus.cs
│ ├── Lock
│ ├── CacheLockProvider.cs
│ ├── DisposableLock.cs
│ ├── DisposableLockCollection.cs
│ ├── ILockProvider.cs
│ ├── ScopedLockProvider.cs
│ └── ThrottlingLockProvider.cs
│ ├── Messaging
│ ├── IMessageBus.cs
│ ├── IMessagePublisher.cs
│ ├── IMessageSubscriber.cs
│ ├── InMemoryMessageBus.cs
│ ├── InMemoryMessageBusOptions.cs
│ ├── Message.cs
│ ├── MessageBusBase.cs
│ ├── NullMessageBus.cs
│ └── SharedMessageBusOptions.cs
│ ├── Metrics
│ └── IHaveSubMetricName.cs
│ ├── Nito.AsyncEx.Coordination
│ ├── AsyncAutoResetEvent.cs
│ ├── AsyncConditionVariable.cs
│ ├── AsyncCountdownEvent.cs
│ ├── AsyncLazy.cs
│ ├── AsyncLock.cs
│ ├── AsyncManualResetEvent.cs
│ ├── AsyncReaderWriterLock.cs
│ ├── AsyncSemaphore.cs
│ ├── AsyncWaitQueue.cs
│ ├── IdManager.cs
│ └── LICENSE
│ ├── Nito.AsyncEx.Tasks
│ ├── AwaitableDisposable.cs
│ ├── CancellationTokenTaskSource.cs
│ ├── ExceptionHelpers.cs
│ ├── LICENSE
│ ├── Synchronous
│ │ └── TaskExtensions.cs
│ ├── TaskCompletionSourceExtensions.cs
│ ├── TaskConstants.cs
│ └── TaskExtensions.cs
│ ├── Nito.Collections.Deque
│ ├── CollectionHelpers.cs
│ ├── Deque.cs
│ └── LICENSE
│ ├── Nito.Disposables
│ ├── AnonymousDisposable.cs
│ ├── Internals
│ │ └── BoundAction.cs
│ ├── LICENSE
│ └── SingleDisposable.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── Queues
│ ├── DuplicateDetectionQueueBehavior.cs
│ ├── IQueue.cs
│ ├── IQueueActivity.cs
│ ├── IQueueEntry.cs
│ ├── InMemoryQueue.cs
│ ├── InMemoryQueueOptions.cs
│ ├── QueueBase.cs
│ ├── QueueBehaviour.cs
│ ├── QueueEntry.cs
│ └── SharedQueueOptions.cs
│ ├── Serializer
│ ├── IHaveSerializer.cs
│ ├── ISerializer.cs
│ └── SystemTextJsonSerializer.cs
│ ├── Storage
│ ├── ActionableStream.cs
│ ├── FolderFileStorage.cs
│ ├── FolderFileStorageOptions.cs
│ ├── IFileStorage.cs
│ ├── InMemoryFileStorage.cs
│ ├── InMemoryFileStorageOptions.cs
│ ├── ScopedFileStorage.cs
│ ├── StorageException.cs
│ └── StreamMode.cs
│ └── Utility
│ ├── AsyncDisposableAction.cs
│ ├── AsyncEvent.cs
│ ├── ConnectionStringParser.cs
│ ├── DataDictionary.cs
│ ├── DisposableAction.cs
│ ├── EmptyDisposable.cs
│ ├── FoundatioDiagnostics.cs
│ ├── IAsyncDisposable.cs
│ ├── IAsyncLifetime.cs
│ ├── IHaveLogger.cs
│ ├── IHaveTimeProvider.cs
│ ├── InstrumentsValues.cs
│ ├── MaintenanceBase.cs
│ ├── OptionsBuilder.cs
│ ├── PathHelper.cs
│ ├── Run.cs
│ ├── ScheduledTimer.cs
│ ├── SharedOptions.cs
│ ├── TimeUnit.cs
│ └── TypeHelper.cs
├── start-all-services.ps1
├── stop-all-services.ps1
└── tests
├── Directory.Build.props
└── Foundatio.Tests
├── Caching
├── InMemoryCacheClientTests.cs
└── InMemoryHybridCacheClientTests.cs
├── Foundatio.Tests.csproj
├── Hosting
├── HostingTests.cs
└── TestServerExtensions.cs
├── Jobs
├── InMemoryJobQueueTests.cs
├── JobTests.cs
└── WorkItemJobTests.cs
├── Locks
└── InMemoryLockTests.cs
├── Messaging
└── InMemoryMessageBusTests.cs
├── Properties
└── AssemblyInfo.cs
├── Queue
└── InMemoryQueueTests.cs
├── Serializer
├── CompressedMessagePackSerializerTests.cs
├── JsonNetSerializerTests.cs
├── MessagePackSerializerTests.cs
├── SystemTextJsonSerializerTests.cs
└── Utf8JsonSerializerTests.cs
├── Storage
├── FolderFileStorageTests.cs
├── InMemoryFileStorageTests.cs
├── ScopedFolderFileStorageTests.cs
└── ScopedInMemoryFileStorageTests.cs
└── Utility
├── CloneTests.cs
├── ConnectionStringParserTests.cs
├── DataDictionaryTests.cs
├── RunTests.cs
├── ScheduledTimerTests.cs
├── TestLoggerTests.cs
└── TestUdpListener.cs
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: exceptionless
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 |
4 | - package-ecosystem: nuget
5 | directory: "/"
6 | schedule:
7 | interval: weekly
8 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | build:
6 | uses: ./.github/workflows/build-workflow.yml
7 | secrets: inherit
8 | with:
9 | solution: Foundatio.sln
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "Code scanning - action"
2 |
3 | on:
4 | schedule:
5 | - cron: '0 1 * * 2'
6 |
7 | jobs:
8 | CodeQL-Build:
9 |
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout repository
14 | uses: actions/checkout@v4
15 | with:
16 | # We must fetch at least the immediate parents so that if this is
17 | # a pull request then we can checkout the head.
18 | fetch-depth: 2
19 |
20 | # If this run was triggered by a pull request event, then checkout
21 | # the head of the pull request instead of the merge commit.
22 | - run: git checkout HEAD^2
23 | if: ${{ github.event_name == 'pull_request' }}
24 |
25 | - name: Initialize CodeQL
26 | uses: github/codeql-action/init@v1
27 | with:
28 | languages: csharp
29 |
30 | - name: Setup .NET Core
31 | uses: actions/setup-dotnet@v4
32 | with:
33 | dotnet-version: 8.0.x
34 |
35 | - name: Build
36 | run: dotnet build Foundatio.sln --configuration Release
37 |
38 | - name: Perform CodeQL Analysis
39 | uses: github/codeql-action/analyze@v1
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # User-specific files
2 | *.suo
3 | *.user
4 |
5 | # Build results
6 | [Bb]in/
7 | [Oo]bj/
8 | artifacts
9 | .vs/
10 |
11 | # MSTest test Results
12 | [Tt]est[Rr]esult*/
13 |
14 | # ReSharper is a .NET coding add-in
15 | _ReSharper*/
16 | *.[Rr]e[Ss]harper
17 | *.DotSettings.user
18 |
19 | # JustCode is a .NET coding addin-in
20 | .JustCode
21 |
22 | # DotCover is a Code Coverage Tool
23 | *.dotCover
24 |
25 | # NCrunch
26 | _NCrunch_*
27 | .*crunch*.local.xml
28 |
29 | # NuGet Packages
30 | *.nupkg
31 |
32 | .DS_Store
33 |
34 | # Rider
35 | .idea
36 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch Hosting Sample",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | // If you have changed target frameworks, make sure to update the program path.
13 | "program": "${workspaceFolder}/samples/Foundatio.HostingSample/bin/Debug/net8.0/Foundatio.HostingSample.dll",
14 | "args": [ ],
15 | "cwd": "${workspaceFolder}/samples/Foundatio.HostingSample",
16 | // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window
17 | "console": "integratedTerminal",
18 | "stopAtEntry": false,
19 | "internalConsoleOptions": "openOnSessionStart"
20 | },
21 | {
22 | "name": ".NET Core Attach",
23 | "type": "coreclr",
24 | "request": "attach",
25 | "processId": "${command:pickProcess}"
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "Dgram",
4 | "Sachin",
5 | "Xunit",
6 | "mygauge",
7 | "unscoped"
8 | ],
9 | "msbuildProjectTools.nuget.includePreRelease": true
10 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "group": {
9 | "kind": "build",
10 | "isDefault": true
11 | },
12 | "args": [
13 | "build",
14 | "${workspaceFolder}/Foundatio.sln",
15 | "/p:GenerateFullPaths=true"
16 | ],
17 | "problemMatcher": "$msCompile"
18 | },
19 | {
20 | "label": "test",
21 | "command": "dotnet",
22 | "type": "process",
23 | "group": {
24 | "kind": "test",
25 | "isDefault": true
26 | },
27 | "args": [
28 | "test",
29 | "${workspaceFolder}/Foundatio.sln",
30 | "/p:GenerateFullPaths=true"
31 | ],
32 | "problemMatcher": "$msCompile"
33 | },
34 | {
35 | "label": "pack",
36 | "command": "dotnet pack -c Release -o ${workspaceFolder}/artifacts",
37 | "type": "shell",
38 | "problemMatcher": []
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/Foundatio.All.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | },
6 | {
7 | "path": "../Foundatio.Repositories"
8 | },
9 | {
10 | "path": "../Foundatio.Redis"
11 | },
12 | {
13 | "path": "../Foundatio.RabbitMQ"
14 | },
15 | {
16 | "path": "../Foundatio.Parsers"
17 | },
18 | {
19 | "path": "../Foundatio.AzureStorage"
20 | },
21 | {
22 | "path": "../Foundatio.AzureServiceBus"
23 | },
24 | {
25 | "path": "../Foundatio.AWS"
26 | },
27 | {
28 | "path": "../Foundatio.Aliyun"
29 | },
30 | {
31 | "path": "../Foundatio.Minio"
32 | },
33 | {
34 | "path": "../Foundatio.Storage.SshNet"
35 | },
36 | {
37 | "path": "../Foundatio.Kafka"
38 | }
39 | ],
40 | "settings": {
41 | "msbuildProjectTools.nuget.includePreRelease": true
42 | }
43 | }
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/build/Foundatio.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FoundatioFx/Foundatio/e79abf6efda75e5fc7006f06b0153e7dbfe31c4d/build/Foundatio.snk
--------------------------------------------------------------------------------
/build/Update-Nito.ps1:
--------------------------------------------------------------------------------
1 | $work_dir = Resolve-Path "$PSScriptRoot"
2 | $src_dir = Resolve-Path "$PSScriptRoot\..\src\Foundatio"
3 |
4 | Function UpdateSource {
5 | param( [string]$sourceUrl, [string]$name )
6 |
7 | If (Test-Path $work_dir\$name.zip) {
8 | Remove-Item $work_dir\$name.zip
9 | }
10 | Invoke-WebRequest $sourceUrl -OutFile $work_dir\$name.zip
11 | If (Test-Path $work_dir\$name) {
12 | Remove-Item $work_dir\$name -Recurse -Force
13 | }
14 | [System.Reflection.Assembly]::LoadWithPartialName("System.IO.Compression.FileSystem") | Out-Null
15 | [System.IO.Compression.ZipFile]::ExtractToDirectory("$work_dir\$name.zip", "$work_dir\$name")
16 |
17 | Remove-Item $work_dir\$name.zip
18 |
19 | If (Test-Path $src_dir\$name) {
20 | Remove-Item $src_dir\$name -Recurse -Force
21 | }
22 |
23 | $dir = (Get-ChildItem $work_dir\$name | Select-Object -First 1).FullName
24 |
25 | New-Item $src_dir\$name -Type Directory
26 | Copy-Item "$dir\LICENSE" -Destination "$src_dir\$name" -Recurse -Force
27 |
28 | Get-ChildItem -Path "$dir\src\$name" -Filter *.cs -Recurse |
29 | Foreach-Object {
30 | $c = ($_ | Get-Content)
31 | $c = $c -replace 'namespace Nito','namespace Foundatio'
32 | $c = $c -replace 'using Nito','using Foundatio'
33 | $c | Set-Content $_.FullName.Replace("$dir\src\$name", "$src_dir\$name")
34 | }
35 |
36 | Remove-Item $work_dir\$name -Recurse -Force
37 | }
38 |
39 | # TODO: Need to update this to pull from master repo and discard unused files.
40 | #UpdateSource "https://github.com/StephenClearyArchive/AsyncEx.Tasks/archive/v1.0.0-delta-4.zip" "Nito.AsyncEx.Tasks"
41 | #UpdateSource "https://github.com/StephenClearyArchive/AsyncEx.Coordination/archive/v1.0.2.zip" "Nito.AsyncEx.Coordination"
42 | UpdateSource "https://github.com/StephenCleary/Disposables/archive/v2.0.1.zip" "Nito.Disposables"
--------------------------------------------------------------------------------
/build/common.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0;net8.0
5 | Foundatio
6 | Pluggable foundation blocks for building distributed apps.
7 | https://github.com/FoundatioFx/Foundatio
8 | https://github.com/FoundatioFx/Foundatio/releases
9 | Queue;Messaging;Message;Bus;ServiceBus;Locking;Lock;Distributed;File;Storage;Blob;Jobs;Metrics;Stats;Azure;Redis;StatsD;Amazon;AWS;S3;broker;Logging;Log
10 | true
11 | v
12 |
13 | Copyright (c) 2025 Foundatio. All rights reserved.
14 | FoundatioFx
15 | $(NoWarn);CS1591;NU1701
16 | true
17 | latest
18 | true
19 | $(SolutionDir)artifacts
20 | foundatio-icon.png
21 | README.md
22 | Apache-2.0
23 | $(PackageProjectUrl)
24 | true
25 | true
26 | embedded
27 |
28 |
29 |
30 | true
31 |
32 |
33 |
34 | true
35 | $(MSBuildThisFileDirectory)Foundatio.snk
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/build/foundatio-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FoundatioFx/Foundatio/e79abf6efda75e5fc7006f06b0153e7dbfe31c4d/build/foundatio-icon.png
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "8.0.100",
4 | "rollForward": "latestMinor",
5 | "allowPrerelease": false
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/media/colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FoundatioFx/Foundatio/e79abf6efda75e5fc7006f06b0153e7dbfe31c4d/media/colors.png
--------------------------------------------------------------------------------
/media/foundatio-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FoundatioFx/Foundatio/e79abf6efda75e5fc7006f06b0153e7dbfe31c4d/media/foundatio-icon.png
--------------------------------------------------------------------------------
/media/foundatio-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FoundatioFx/Foundatio/e79abf6efda75e5fc7006f06b0153e7dbfe31c4d/media/foundatio-white.png
--------------------------------------------------------------------------------
/media/foundatio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FoundatioFx/Foundatio/e79abf6efda75e5fc7006f06b0153e7dbfe31c4d/media/foundatio.png
--------------------------------------------------------------------------------
/samples/Foundatio.AppHost/Extensions/ImageTags.cs:
--------------------------------------------------------------------------------
1 | namespace Foundatio.AppHost.Extensions;
2 |
3 | public static class ImageTags
4 | {
5 | public const string Redis = "7.4-alpine";
6 | }
7 |
--------------------------------------------------------------------------------
/samples/Foundatio.AppHost/Extensions/RedisExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Diagnostics.HealthChecks;
2 | using StackExchange.Redis;
3 |
4 | namespace Foundatio.AppHost.Extensions;
5 |
6 | public static class RedisExtensions
7 | {
8 | public static IResourceBuilder WithClearCommand(
9 | this IResourceBuilder builder)
10 | {
11 | builder.WithCommand(
12 | "clear-cache",
13 | "Clear Cache",
14 | async _ =>
15 | {
16 | var redisConnectionString = await builder.Resource.GetConnectionStringAsync() ??
17 | throw new InvalidOperationException("Unable to get the Redis connection string.");
18 |
19 | await using var connection = await ConnectionMultiplexer.ConnectAsync(redisConnectionString);
20 |
21 | await connection.GetDatabase().ExecuteAsync("FLUSHALL");
22 |
23 | return CommandResults.Success();
24 | },
25 | new CommandOptions
26 | {
27 | UpdateState = context => context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy ? ResourceCommandState.Enabled : ResourceCommandState.Disabled
28 | });
29 | return builder;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/samples/Foundatio.AppHost/Foundatio.AppHost.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Exe
7 | net8.0
8 | enable
9 | enable
10 | 96cfcbc9-36fb-452f-9b99-0165197e1978
11 | False
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/samples/Foundatio.AppHost/Program.cs:
--------------------------------------------------------------------------------
1 | using Foundatio.AppHost.Extensions;
2 | using Projects;
3 | #pragma warning disable ASPIREPROXYENDPOINTS001
4 |
5 | var builder = DistributedApplication.CreateBuilder(args);
6 |
7 | var cache = builder.AddRedis("Redis", port: 6379)
8 | .WithContainerName("Foundatio-Redis")
9 | .WithImageTag(ImageTags.Redis)
10 | .WithEndpointProxySupport(false)
11 | .WithClearCommand()
12 | .WithRedisInsight(b => b.WithEndpointProxySupport(false).WithContainerName("Foundatio-RedisInsight")
13 | .WithUrlForEndpoint("http", u => u.DisplayText = "Cache"));
14 |
15 | builder.AddProject("Foundatio-HostingSample")
16 | .WithExternalHttpEndpoints()
17 | .WithReplicas(3)
18 | .WithReference(cache)
19 | .WaitFor(cache)
20 | .WithArgs("all")
21 | .WithUrls(u =>
22 | {
23 | u.Urls.Clear();
24 | u.Urls.Add(new ResourceUrlAnnotation { Url = "/jobs/status", DisplayText = "Job Status", Endpoint = u.GetEndpoint("http") });
25 | u.Urls.Add(new ResourceUrlAnnotation { Url = "/jobs/run", DisplayText = "Run Job", Endpoint = u.GetEndpoint("http") });
26 | });
27 |
28 | builder.Build().Run();
29 |
--------------------------------------------------------------------------------
/samples/Foundatio.AppHost/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "https": {
5 | "commandName": "Project",
6 | "dotnetRunMessages": true,
7 | "launchBrowser": true,
8 | "applicationUrl": "https://localhost:17176;http://localhost:15249",
9 | "environmentVariables": {
10 | "ASPNETCORE_ENVIRONMENT": "Development",
11 | "DOTNET_ENVIRONMENT": "Development",
12 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21196",
13 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22113"
14 | }
15 | },
16 | "http": {
17 | "commandName": "Project",
18 | "dotnetRunMessages": true,
19 | "launchBrowser": true,
20 | "applicationUrl": "http://localhost:15249",
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development",
23 | "DOTNET_ENVIRONMENT": "Development",
24 | "DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19180",
25 | "DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20234"
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/samples/Foundatio.AppHost/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/Foundatio.AppHost/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning",
6 | "Aspire.Hosting.Dcp": "Warning"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Foundatio.HostingSample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | False
6 | false
7 | REDIS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Jobs/EveryMinuteJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Caching;
5 | using Foundatio.Jobs;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace Foundatio.HostingSample;
9 |
10 | public class EveryMinuteJob : IJob
11 | {
12 | private readonly ICacheClient _cacheClient;
13 | private readonly ILogger _logger;
14 |
15 | public EveryMinuteJob(ILoggerFactory loggerFactory, ICacheClient cacheClient)
16 | {
17 | _cacheClient = cacheClient;
18 | _logger = loggerFactory.CreateLogger();
19 | }
20 |
21 | public async Task RunAsync(CancellationToken cancellationToken = default)
22 | {
23 | var runCount = await _cacheClient.IncrementAsync("EveryMinuteJob");
24 |
25 | _logger.LogInformation("EveryMinuteJob Run Count={Count} Thread={ManagedThreadId}", runCount, Thread.CurrentThread.ManagedThreadId);
26 |
27 | await Task.Delay(TimeSpan.FromSeconds(30));
28 |
29 | _logger.LogInformation("EveryMinuteJob Complete");
30 |
31 | return JobResult.Success;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Jobs/Sample1Job.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Foundatio.Jobs;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Foundatio.HostingSample;
7 |
8 | [Job(Description = "Sample 1 job", Interval = "5s", IterationLimit = 5)]
9 | public class Sample1Job : IJob
10 | {
11 | private readonly ILogger _logger;
12 | private int _iterationCount = 0;
13 |
14 | public Sample1Job(ILoggerFactory loggerFactory)
15 | {
16 | _logger = loggerFactory.CreateLogger();
17 | }
18 |
19 | public Task RunAsync(CancellationToken cancellationToken = default)
20 | {
21 | Interlocked.Increment(ref _iterationCount);
22 | _logger.LogTrace("Sample1Job Run #{IterationCount} Thread={ManagedThreadId}", _iterationCount, Thread.CurrentThread.ManagedThreadId);
23 |
24 | return Task.FromResult(JobResult.Success);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Jobs/Sample2Job.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Jobs;
5 | using Microsoft.Extensions.Diagnostics.HealthChecks;
6 | using Microsoft.Extensions.Logging;
7 |
8 | namespace Foundatio.HostingSample;
9 |
10 | [Job(Description = "Sample 2 job", Interval = "15s", IterationLimit = 24)]
11 | public class Sample2Job : IJob, IHealthCheck
12 | {
13 | private readonly ILogger _logger;
14 | private int _iterationCount = 0;
15 | private DateTime? _lastRun = null;
16 |
17 | public Sample2Job(ILoggerFactory loggerFactory)
18 | {
19 | _logger = loggerFactory.CreateLogger();
20 | }
21 |
22 | public Task RunAsync(CancellationToken cancellationToken = default)
23 | {
24 | _lastRun = DateTime.UtcNow;
25 | Interlocked.Increment(ref _iterationCount);
26 | _logger.LogTrace("Sample2Job Run #{IterationCount} Thread={ManagedThreadId}", _iterationCount, Thread.CurrentThread.ManagedThreadId);
27 |
28 | return Task.FromResult(JobResult.Success);
29 | }
30 |
31 | public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
32 | {
33 | if (!_lastRun.HasValue)
34 | return Task.FromResult(HealthCheckResult.Healthy("Job has not been run yet."));
35 |
36 | if (DateTime.UtcNow.Subtract(_lastRun.Value) > TimeSpan.FromSeconds(5))
37 | return Task.FromResult(HealthCheckResult.Unhealthy("Job has not run in the last 5 seconds."));
38 |
39 | return Task.FromResult(HealthCheckResult.Healthy("Job has run in the last 5 seconds."));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Jobs/SampleLockJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Caching;
5 | using Foundatio.Jobs;
6 | using Foundatio.Lock;
7 | using Microsoft.Extensions.Logging;
8 |
9 | namespace Foundatio.HostingSample;
10 |
11 | [Job(Description = "Sample lock job", Interval = "5s")]
12 | public class SampleLockJob : JobWithLockBase
13 | {
14 | private readonly ILockProvider _lockProvider;
15 |
16 | public SampleLockJob(ICacheClient cache)
17 | {
18 | _lockProvider = new ThrottlingLockProvider(cache, 1, TimeSpan.FromMinutes(1));
19 | }
20 |
21 | protected override Task GetLockAsync(CancellationToken cancellationToken = default)
22 | {
23 | return _lockProvider.AcquireAsync(nameof(SampleLockJob), TimeSpan.FromMinutes(15), new CancellationToken(true));
24 | }
25 |
26 | protected override Task RunInternalAsync(JobContext context)
27 | {
28 | _logger.LogTrace("SampleLockJob Run Thread={ManagedThreadId}", Thread.CurrentThread.ManagedThreadId);
29 |
30 | return Task.FromResult(JobResult.Success);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/MyCriticalHealthCheck.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Microsoft.Extensions.Diagnostics.HealthChecks;
5 |
6 | namespace Foundatio.HostingSample;
7 |
8 | public class MyCriticalHealthCheck : IHealthCheck
9 | {
10 | private static DateTime _startTime = DateTime.Now;
11 |
12 | public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = new CancellationToken())
13 | {
14 | return DateTime.Now.Subtract(_startTime) > TimeSpan.FromSeconds(3) ?
15 | Task.FromResult(HealthCheckResult.Healthy("Critical resource is available."))
16 | : Task.FromResult(HealthCheckResult.Unhealthy("Critical resource not available."));
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/launchsettings.json",
3 | "profiles": {
4 | "http": {
5 | "commandName": "Project",
6 | "commandLineArgs": "all",
7 | "dotnetRunMessages": true,
8 | "launchBrowser": false,
9 | "launchUrl": "jobstatus",
10 | "applicationUrl": "http://localhost:5324",
11 | "environmentVariables": {
12 | "ASPNETCORE_ENVIRONMENT": "Development"
13 | }
14 | },
15 | "https": {
16 | "commandName": "Project",
17 | "commandLineArgs": "all",
18 | "dotnetRunMessages": true,
19 | "launchBrowser": false,
20 | "launchUrl": "jobstatus",
21 | "applicationUrl": "https://localhost:7580;http://localhost:5324",
22 | "environmentVariables": {
23 | "ASPNETCORE_ENVIRONMENT": "Development"
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/ServiceDefaults.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 | using Microsoft.Extensions.Logging;
3 | using OpenTelemetry;
4 | using OpenTelemetry.Metrics;
5 | using OpenTelemetry.Trace;
6 |
7 | namespace Microsoft.Extensions.Hosting;
8 |
9 | public static class Extensions
10 | {
11 | public static TBuilder AddServiceDefaults(this TBuilder builder) where TBuilder : IHostApplicationBuilder
12 | {
13 | builder.ConfigureOpenTelemetry();
14 |
15 | return builder;
16 | }
17 |
18 | public static TBuilder ConfigureOpenTelemetry(this TBuilder builder) where TBuilder : IHostApplicationBuilder
19 | {
20 | builder.Logging.AddOpenTelemetry(logging =>
21 | {
22 | logging.IncludeFormattedMessage = true;
23 | logging.IncludeScopes = true;
24 | });
25 |
26 | builder.Services.AddOpenTelemetry()
27 | .WithMetrics(metrics =>
28 | {
29 | metrics.AddAspNetCoreInstrumentation()
30 | .AddHttpClientInstrumentation()
31 | .AddRuntimeInstrumentation()
32 | .AddMeter("Foundatio");
33 | })
34 | .WithTracing(tracing =>
35 | {
36 | tracing.AddAspNetCoreInstrumentation()
37 | .AddHttpClientInstrumentation()
38 | .AddSource("Foundatio");
39 | });
40 |
41 | builder.AddOpenTelemetryExporters();
42 |
43 | return builder;
44 | }
45 |
46 | private static TBuilder AddOpenTelemetryExporters(this TBuilder builder) where TBuilder : IHostApplicationBuilder
47 | {
48 | var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
49 |
50 | if (useOtlpExporter)
51 | {
52 | builder.Services.AddOpenTelemetry().UseOtlpExporter();
53 | }
54 |
55 | return builder;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Startup/MyStartupAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Caching;
5 | using Foundatio.Extensions.Hosting.Jobs;
6 | using Foundatio.Extensions.Hosting.Startup;
7 | using Microsoft.Extensions.Logging;
8 |
9 | namespace Foundatio.HostingSample;
10 |
11 | public class MyStartupAction : IStartupAction
12 | {
13 | private readonly IJobManager _jobManager;
14 | private readonly ICacheClient _cacheClient;
15 | private readonly ILogger _logger;
16 |
17 | public MyStartupAction(IJobManager jobManager, ICacheClient cacheClient, ILogger logger)
18 | {
19 | _jobManager = jobManager;
20 | _cacheClient = cacheClient;
21 | _logger = logger;
22 | }
23 |
24 | public async Task RunAsync(CancellationToken cancellationToken = default)
25 | {
26 | // set next run to be far in the past so it runs immediately
27 | await _cacheClient.SetAsync("jobs:every_minute:nextrun", DateTime.UtcNow.AddDays(-1));
28 |
29 | for (int i = 0; i < 5; i++)
30 | {
31 | _logger.LogTrace("MyStartupAction Run Thread={ManagedThreadId}", Thread.CurrentThread.ManagedThreadId);
32 | await Task.Delay(500);
33 | }
34 |
35 | _jobManager.AddOrUpdate("MyJob", j => j.CronSchedule("* * * * *").JobAction(async () =>
36 | {
37 | _logger.LogInformation("Running MyJob");
38 | await Task.Delay(1000);
39 | _logger.LogInformation("MyJob Complete");
40 | }));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/Startup/OtherStartupAction.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using Foundatio.Extensions.Hosting.Startup;
4 | using Microsoft.Extensions.Logging;
5 |
6 | namespace Foundatio.HostingSample;
7 |
8 | public class OtherStartupAction : IStartupAction
9 | {
10 | private readonly ILogger _logger;
11 |
12 | public OtherStartupAction(ILogger logger)
13 | {
14 | _logger = logger;
15 | }
16 |
17 | public async Task RunAsync(CancellationToken cancellationToken = default)
18 | {
19 | for (int i = 0; i < 5; i++)
20 | {
21 | _logger.LogTrace("OtherStartupAction Run Thread={ManagedThreadId}", Thread.CurrentThread.ManagedThreadId);
22 | await Task.Delay(900);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/Foundatio.HostingSample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft.AspNetCore": "Warning",
6 | "Foundatio": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*"
10 | }
11 |
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/Foundatio.DataProtection/Foundatio.DataProtection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(PackageTags);DataProtection
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Cronos/CronExpressionFlag.cs:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright (c) 2017 Sergey Odinokov
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | using System;
24 |
25 | namespace Foundatio.Extensions.Hosting.Cronos;
26 |
27 | [Flags]
28 | internal enum CronExpressionFlag : byte
29 | {
30 | DayOfMonthLast = 0b00001,
31 | DayOfWeekLast = 0b00010,
32 | Interval = 0b00100,
33 | NearestWeekday = 0b01000,
34 | NthDayOfWeek = 0b10000
35 | }
36 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Cronos/CronFormat.cs:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright (c) 2017 Sergey Odinokov
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | using System;
24 |
25 | namespace Foundatio.Extensions.Hosting.Cronos;
26 |
27 | ///
28 | /// Defines the cron format options that customize string parsing for .
29 | ///
30 | [Flags]
31 | public enum CronFormat
32 | {
33 | ///
34 | /// Parsing string must contain only 5 fields: minute, hour, day of month, month, day of week.
35 | ///
36 | Standard = 0,
37 |
38 | ///
39 | /// Second field must be specified in parsing string.
40 | ///
41 | IncludeSeconds = 1
42 | }
43 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Cronos/CronFormatException.cs:
--------------------------------------------------------------------------------
1 | // The MIT License(MIT)
2 | //
3 | // Copyright (c) 2017 Sergey Odinokov
4 | //
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included in all
13 | // copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | using System;
24 |
25 | namespace Foundatio.Extensions.Hosting.Cronos;
26 |
27 | ///
28 | /// Represents an exception that's thrown, when invalid Cron expression is given.
29 | ///
30 | #if !NETSTANDARD1_0
31 | [Serializable]
32 | #endif
33 | public class CronFormatException : FormatException
34 | {
35 | ///
36 | /// Initializes a new instance of the class with
37 | /// the given message.
38 | ///
39 | public CronFormatException(string message) : base(message)
40 | {
41 | }
42 |
43 | internal CronFormatException(CronField field, string message) : this($"{field}: {message}")
44 | {
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Foundatio.Extensions.Hosting.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | net8.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Jobs/DynamicJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Jobs;
5 | using Foundatio.Utility;
6 |
7 | namespace Foundatio.Extensions.Hosting.Jobs;
8 |
9 | internal class DynamicJob : IJob
10 | {
11 | private readonly IServiceProvider _serviceProvider;
12 | private readonly Func _action;
13 |
14 | public DynamicJob(IServiceProvider serviceProvider, Func action)
15 | {
16 | _serviceProvider = serviceProvider;
17 | _action = action;
18 | }
19 |
20 | public async Task RunAsync(CancellationToken cancellationToken = default)
21 | {
22 | await _action(_serviceProvider, cancellationToken).AnyContext();
23 |
24 | return JobResult.Success;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Jobs/HostedJobOptions.cs:
--------------------------------------------------------------------------------
1 | using Foundatio.Jobs;
2 |
3 | namespace Foundatio.Extensions.Hosting.Jobs;
4 |
5 | public class HostedJobOptions : JobOptions
6 | {
7 | public bool WaitForStartupActions { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Jobs/JobOptionsBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Foundatio.Jobs;
3 |
4 | namespace Foundatio.Extensions.Hosting.Jobs;
5 |
6 | public class HostedJobOptionsBuilder
7 | {
8 | public HostedJobOptionsBuilder(HostedJobOptions target = null)
9 | {
10 | Target = target ?? new HostedJobOptions();
11 | }
12 |
13 | public HostedJobOptions Target { get; }
14 |
15 | public HostedJobOptionsBuilder ApplyDefaults() where T : IJob
16 | {
17 | Target.ApplyDefaults();
18 | return this;
19 | }
20 |
21 | public HostedJobOptionsBuilder ApplyDefaults(Type jobType)
22 | {
23 | JobOptions.ApplyDefaults(Target, jobType);
24 | return this;
25 | }
26 |
27 | public HostedJobOptionsBuilder Name(string value)
28 | {
29 | Target.Name = value;
30 | return this;
31 | }
32 |
33 | public HostedJobOptionsBuilder Description(string value)
34 | {
35 | Target.Description = value;
36 | return this;
37 | }
38 |
39 | public HostedJobOptionsBuilder JobFactory(Func value)
40 | {
41 | Target.JobFactory = value;
42 | return this;
43 | }
44 |
45 | public HostedJobOptionsBuilder RunContinuous(bool value = true)
46 | {
47 | Target.RunContinuous = value;
48 | return this;
49 | }
50 |
51 | public HostedJobOptionsBuilder Interval(TimeSpan? value)
52 | {
53 | Target.Interval = value;
54 | return this;
55 | }
56 |
57 | public HostedJobOptionsBuilder InitialDelay(TimeSpan? value)
58 | {
59 | Target.InitialDelay = value;
60 | return this;
61 | }
62 |
63 | public HostedJobOptionsBuilder IterationLimit(int value)
64 | {
65 | Target.IterationLimit = value;
66 | return this;
67 | }
68 |
69 | public HostedJobOptionsBuilder InstanceCount(int value)
70 | {
71 | Target.InstanceCount = value;
72 | return this;
73 | }
74 |
75 | public HostedJobOptionsBuilder WaitForStartupActions(bool value = true)
76 | {
77 | Target.WaitForStartupActions = value;
78 | return this;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Jobs/ScheduledJobOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Runtime.CompilerServices;
4 | using Foundatio.Jobs;
5 |
6 | namespace Foundatio.Extensions.Hosting.Jobs;
7 |
8 | public class ScheduledJobOptions : INotifyPropertyChanged
9 | {
10 | private string _name;
11 | private string _description;
12 | private Func _jobFactory;
13 | private bool _waitForStartupActions;
14 | private string _cronSchedule;
15 | private TimeZoneInfo _cronTimeZone;
16 | private bool _isDistributed;
17 | private bool _isEnabled = true;
18 |
19 | public string Name
20 | {
21 | get => _name;
22 | set => SetField(ref _name, value);
23 | }
24 |
25 | public string Description
26 | {
27 | get => _description;
28 | set => SetField(ref _description, value);
29 | }
30 |
31 | public Func JobFactory
32 | {
33 | get => _jobFactory;
34 | set => SetField(ref _jobFactory, value);
35 | }
36 |
37 | public bool WaitForStartupActions
38 | {
39 | get => _waitForStartupActions;
40 | set => SetField(ref _waitForStartupActions, value);
41 | }
42 |
43 | public string CronSchedule
44 | {
45 | get => _cronSchedule;
46 | set => SetField(ref _cronSchedule, value);
47 | }
48 |
49 | public TimeZoneInfo CronTimeZone
50 | {
51 | get => _cronTimeZone;
52 | set => SetField(ref _cronTimeZone, value);
53 | }
54 |
55 | public bool IsDistributed
56 | {
57 | get => _isDistributed;
58 | set => SetField(ref _isDistributed, value);
59 | }
60 |
61 | public bool IsEnabled
62 | {
63 | get => _isEnabled;
64 | set => SetField(ref _isEnabled, value);
65 | }
66 |
67 | public event PropertyChangedEventHandler PropertyChanged;
68 |
69 | private bool SetField(ref T field, T value, [CallerMemberName] string propertyName = "")
70 | {
71 | if (Equals(field, value))
72 | return false;
73 |
74 | field = value;
75 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
76 | return true;
77 | }
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Jobs/ScheduledJobRegistration.cs:
--------------------------------------------------------------------------------
1 | namespace Foundatio.Extensions.Hosting.Jobs;
2 |
3 | public class ScheduledJobRegistration
4 | {
5 | public ScheduledJobRegistration(ScheduledJobOptions options)
6 | {
7 | Options = options;
8 | }
9 |
10 | public ScheduledJobOptions Options { get; private set; }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Startup/IStartupAction.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 |
4 | namespace Foundatio.Extensions.Hosting.Startup;
5 |
6 | public interface IStartupAction
7 | {
8 | Task RunAsync(CancellationToken shutdownToken = default);
9 | }
10 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Startup/RunStartupActionsService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Utility;
5 | using Microsoft.Extensions.Hosting;
6 |
7 | namespace Foundatio.Extensions.Hosting.Startup;
8 |
9 | public class RunStartupActionsService : BackgroundService
10 | {
11 | private readonly StartupActionsContext _startupContext;
12 | private readonly IServiceProvider _serviceProvider;
13 |
14 | public RunStartupActionsService(StartupActionsContext startupContext, IServiceProvider serviceProvider)
15 | {
16 | _startupContext = startupContext;
17 | _serviceProvider = serviceProvider;
18 | }
19 |
20 | protected override async Task ExecuteAsync(CancellationToken stoppingToken)
21 | {
22 | var result = await _serviceProvider.RunStartupActionsAsync(stoppingToken).AnyContext();
23 | _startupContext.MarkStartupComplete(result);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Startup/StartupActionRegistration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using Foundatio.Utility;
6 | using Microsoft.Extensions.DependencyInjection;
7 |
8 | namespace Foundatio.Extensions.Hosting.Startup;
9 |
10 | public class StartupActionRegistration
11 | {
12 | private readonly Func _action;
13 | private readonly Type _actionType;
14 | private static int _currentAutoPriority;
15 |
16 | public StartupActionRegistration(string name, Type startupType, int? priority = null)
17 | {
18 | Name = name;
19 | _actionType = startupType;
20 | if (!priority.HasValue)
21 | {
22 | var priorityAttribute = _actionType.GetCustomAttributes(typeof(StartupPriorityAttribute), true).FirstOrDefault() as StartupPriorityAttribute;
23 | Priority = priorityAttribute?.Priority ?? Interlocked.Increment(ref _currentAutoPriority);
24 | }
25 | else
26 | {
27 | Priority = priority.Value;
28 | }
29 | }
30 |
31 | public StartupActionRegistration(string name, Func action, int? priority = null)
32 | {
33 | Name = name;
34 | _action = action;
35 | if (!priority.HasValue)
36 | priority = Interlocked.Increment(ref _currentAutoPriority);
37 |
38 | Priority = priority.Value;
39 | }
40 |
41 | public string Name { get; private set; }
42 |
43 | public int Priority { get; private set; }
44 |
45 | public async Task RunAsync(IServiceProvider serviceProvider, CancellationToken shutdownToken = default)
46 | {
47 | if (shutdownToken.IsCancellationRequested)
48 | return;
49 |
50 | if (_actionType != null)
51 | {
52 | if (serviceProvider.GetRequiredService(_actionType) is IStartupAction startup)
53 | await startup.RunAsync(shutdownToken).AnyContext();
54 | }
55 | else
56 | {
57 | await _action(serviceProvider, shutdownToken).AnyContext();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Startup/StartupActionsContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Utility;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Foundatio.Extensions.Hosting.Startup;
8 |
9 | public class StartupActionsContext
10 | {
11 | private readonly ILogger _logger;
12 | private int _waitCount = 0;
13 |
14 | public StartupActionsContext(ILogger logger)
15 | {
16 | _logger = logger;
17 | }
18 |
19 | public bool IsStartupComplete { get; private set; }
20 | public RunStartupActionsResult Result { get; private set; }
21 |
22 | internal void MarkStartupComplete(RunStartupActionsResult result)
23 | {
24 | IsStartupComplete = true;
25 | Result = result;
26 | }
27 |
28 | public async Task WaitForStartupAsync(CancellationToken cancellationToken, TimeSpan? maxTimeToWait = null)
29 | {
30 | bool isFirstWaiter = Interlocked.Increment(ref _waitCount) == 1;
31 | var startTime = DateTime.UtcNow;
32 | var lastStatus = DateTime.UtcNow;
33 | maxTimeToWait ??= TimeSpan.FromMinutes(5);
34 |
35 | while (!cancellationToken.IsCancellationRequested && DateTime.UtcNow.Subtract(startTime) < maxTimeToWait)
36 | {
37 | if (IsStartupComplete)
38 | return Result;
39 |
40 | if (isFirstWaiter && DateTime.UtcNow.Subtract(lastStatus) > TimeSpan.FromSeconds(5))
41 | {
42 | lastStatus = DateTime.UtcNow;
43 | _logger.LogInformation("Waiting for startup actions to be completed for {Duration:mm\\:ss}...", DateTime.UtcNow.Subtract(startTime));
44 | }
45 |
46 | await Task.Delay(1000, cancellationToken).AnyContext();
47 | }
48 |
49 | if (isFirstWaiter)
50 | _logger.LogError("Timed out waiting for startup actions to be completed after {Duration:mm\\:ss}", DateTime.UtcNow.Subtract(startTime));
51 |
52 | return new RunStartupActionsResult { Success = false, ErrorMessage = $"Timed out waiting for startup actions to be completed after {DateTime.UtcNow.Subtract(startTime):mm\\:ss}" };
53 | }
54 | }
55 |
56 | public class StartupActionsException : Exception
57 | {
58 | public StartupActionsException(string message) : base(message) { }
59 | }
60 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Startup/StartupHealthcheck.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Diagnostics.HealthChecks;
6 |
7 | namespace Foundatio.Extensions.Hosting.Startup;
8 |
9 | public class StartupActionsHealthCheck : IHealthCheck
10 | {
11 | private readonly IServiceProvider _serviceProvider;
12 |
13 | public StartupActionsHealthCheck(IServiceProvider serviceProvider)
14 | {
15 | _serviceProvider = serviceProvider;
16 | }
17 |
18 | public Task CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
19 | {
20 | var startupContext = _serviceProvider.GetService();
21 |
22 | // no startup actions registered
23 | if (startupContext == null)
24 | return Task.FromResult(HealthCheckResult.Healthy("No startup actions registered"));
25 |
26 | if (startupContext.IsStartupComplete && startupContext.Result.Success)
27 | return Task.FromResult(HealthCheckResult.Healthy("All startup actions completed"));
28 |
29 | if (startupContext.IsStartupComplete && !startupContext.Result.Success)
30 | return Task.FromResult(HealthCheckResult.Unhealthy($"Startup action \"{startupContext.Result.FailedActionName}\" failed to complete: {startupContext.Result.ErrorMessage}"));
31 |
32 | return Task.FromResult(HealthCheckResult.Unhealthy("Startup actions have not completed"));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Startup/StartupPriorityAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Foundatio.Extensions.Hosting.Startup;
4 |
5 | public class StartupPriorityAttribute : Attribute
6 | {
7 | public StartupPriorityAttribute(int priority)
8 | {
9 | Priority = priority;
10 | }
11 |
12 | public int Priority { get; private set; }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Foundatio.Extensions.Hosting/Startup/WaitForStartupActionsBeforeServingRequestsMiddleware.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Foundatio.Utility;
4 | using Microsoft.AspNetCore.Http;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 |
8 | namespace Foundatio.Extensions.Hosting.Startup;
9 |
10 | public class WaitForStartupActionsBeforeServingRequestsMiddleware
11 | {
12 | private readonly IServiceProvider _serviceProvider;
13 | private readonly RequestDelegate _next;
14 | private readonly IHostApplicationLifetime _applicationLifetime;
15 |
16 | public WaitForStartupActionsBeforeServingRequestsMiddleware(IServiceProvider serviceProvider, RequestDelegate next, IHostApplicationLifetime applicationLifetime)
17 | {
18 | _serviceProvider = serviceProvider;
19 | _next = next;
20 | _applicationLifetime = applicationLifetime;
21 | }
22 |
23 | public async Task Invoke(HttpContext httpContext)
24 | {
25 | var startupContext = _serviceProvider.GetService();
26 |
27 | // no startup actions registered
28 | if (startupContext == null)
29 | {
30 | await _next(httpContext).AnyContext();
31 | return;
32 | }
33 |
34 | if (startupContext.IsStartupComplete && startupContext.Result.Success)
35 | {
36 | await _next(httpContext).AnyContext();
37 | }
38 | else if (startupContext.IsStartupComplete && !startupContext.Result.Success)
39 | {
40 | // kill the server if the startup actions failed
41 | _applicationLifetime.StopApplication();
42 | }
43 | else
44 | {
45 | httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
46 | httpContext.Response.Headers["Retry-After"] = "10";
47 | await httpContext.Response.WriteAsync("Service Unavailable").AnyContext();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Foundatio.JsonNet/Foundatio.JsonNet.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/Foundatio.JsonNet/JsonNetSerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Newtonsoft.Json;
4 |
5 | namespace Foundatio.Serializer;
6 |
7 | public class JsonNetSerializer : ITextSerializer
8 | {
9 | private readonly JsonSerializer _serializer;
10 |
11 | public JsonNetSerializer(JsonSerializerSettings settings = null)
12 | {
13 | _serializer = JsonSerializer.Create(settings ?? new JsonSerializerSettings());
14 | }
15 |
16 | public void Serialize(object data, Stream outputStream)
17 | {
18 | var writer = new JsonTextWriter(new StreamWriter(outputStream));
19 | _serializer.Serialize(writer, data, data.GetType());
20 | writer.Flush();
21 | }
22 |
23 | public object Deserialize(Stream inputStream, Type objectType)
24 | {
25 | using var sr = new StreamReader(inputStream);
26 | using var reader = new JsonTextReader(sr);
27 | return _serializer.Deserialize(reader, objectType);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Foundatio.MessagePack/Foundatio.MessagePack.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/Foundatio.MessagePack/MessagePackSerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using MessagePack;
4 | using MessagePack.Resolvers;
5 |
6 | namespace Foundatio.Serializer;
7 |
8 | public class MessagePackSerializer : ISerializer
9 | {
10 | private readonly MessagePackSerializerOptions _options;
11 |
12 | public MessagePackSerializer(MessagePackSerializerOptions options = null)
13 | {
14 | _options = options ?? MessagePackSerializerOptions.Standard.WithResolver(ContractlessStandardResolver.Instance);
15 | }
16 |
17 | public void Serialize(object data, Stream output)
18 | {
19 | MessagePack.MessagePackSerializer.Serialize(data.GetType(), output, data, _options);
20 | }
21 |
22 | public object Deserialize(Stream input, Type objectType)
23 | {
24 | return MessagePack.MessagePackSerializer.Deserialize(objectType, input, _options);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Extensions/TaskExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading.Tasks;
4 | using Foundatio.AsyncEx;
5 | using Foundatio.Utility;
6 |
7 | namespace Foundatio.Tests.Extensions;
8 |
9 | public static class TaskExtensions
10 | {
11 | [DebuggerStepThrough]
12 | public static async Task WaitAsync(this AsyncManualResetEvent resetEvent, TimeSpan timeout)
13 | {
14 | using var timeoutCancellationTokenSource = timeout.ToCancellationTokenSource();
15 | await resetEvent.WaitAsync(timeoutCancellationTokenSource.Token).AnyContext();
16 | }
17 |
18 | [DebuggerStepThrough]
19 | public static async Task WaitAsync(this AsyncAutoResetEvent resetEvent, TimeSpan timeout)
20 | {
21 | using var timeoutCancellationTokenSource = timeout.ToCancellationTokenSource();
22 | await resetEvent.WaitAsync(timeoutCancellationTokenSource.Token).AnyContext();
23 | }
24 |
25 | public static Task WaitAsync(this AsyncCountdownEvent countdownEvent, TimeSpan timeout)
26 | {
27 | return Task.WhenAny(countdownEvent.WaitAsync(), Task.Delay(timeout));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Foundatio.TestHarness.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0
4 | true
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/GlobalSuppressions.cs:
--------------------------------------------------------------------------------
1 |
2 | // This file is used by Code Analysis to maintain SuppressMessage
3 | // attributes that are applied to this project.
4 | // Project-level suppressions either have no target or are given
5 | // a specific target and scoped to a namespace, type, member, etc.
6 |
7 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("AsyncUsage", "AsyncFixer02:Long running or blocking operations under an async method", Justification = "", Scope = "member", Target = "~M:Foundatio.Tests.Storage.FileStorageTestsBase.CanSaveFilesAsync~System.Threading.Tasks.Task")]
8 |
9 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Jobs/HelloWorldJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Jobs;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace Foundatio.Tests.Jobs;
8 |
9 | public class HelloWorldJob : JobBase
10 | {
11 | private readonly string _id;
12 |
13 | public HelloWorldJob(TimeProvider timeProvider, ILoggerFactory loggerFactory) : base(timeProvider, loggerFactory)
14 | {
15 | _id = Guid.NewGuid().ToString("N").Substring(0, 10);
16 | }
17 |
18 | public static int GlobalRunCount;
19 | public int RunCount { get; set; }
20 |
21 | protected override Task RunInternalAsync(JobContext context)
22 | {
23 | RunCount++;
24 | Interlocked.Increment(ref GlobalRunCount);
25 |
26 | _logger.LogTrace("HelloWorld Running: instance={Id} runs={RunCount} global={GlobalRunCount}", _id, RunCount, GlobalRunCount);
27 |
28 | return Task.FromResult(JobResult.Success);
29 | }
30 | }
31 |
32 | public class FailingJob : JobBase
33 | {
34 | private readonly string _id;
35 |
36 | public int RunCount { get; set; }
37 |
38 | public FailingJob(TimeProvider timeProvider, ILoggerFactory loggerFactory) : base(timeProvider, loggerFactory)
39 | {
40 | _id = Guid.NewGuid().ToString("N").Substring(0, 10);
41 | }
42 |
43 | protected override Task RunInternalAsync(JobContext context)
44 | {
45 | RunCount++;
46 |
47 | _logger.LogTrace("FailingJob Running: instance={Id} runs={RunCount}", _id, RunCount);
48 |
49 | return Task.FromResult(JobResult.FailedWithMessage("Test failure"));
50 | }
51 | }
52 |
53 | public class LongRunningJob : JobBase
54 | {
55 | private readonly string _id;
56 | private int _iterationCount;
57 |
58 | public LongRunningJob(TimeProvider timeProvider, ILoggerFactory loggerFactory) : base(timeProvider, loggerFactory)
59 | {
60 | _id = Guid.NewGuid().ToString("N").Substring(0, 10);
61 | }
62 |
63 | public int IterationCount => _iterationCount;
64 |
65 | protected override Task RunInternalAsync(JobContext context)
66 | {
67 | do
68 | {
69 | Interlocked.Increment(ref _iterationCount);
70 | if (context.CancellationToken.IsCancellationRequested)
71 | break;
72 |
73 | if (_iterationCount % 10000 == 0)
74 | _logger.LogTrace("LongRunningJob Running: instance={Id} iterations={IterationCount}", _id, IterationCount);
75 | } while (true);
76 |
77 | return Task.FromResult(JobResult.Success);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Jobs/ThrottledJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Caching;
5 | using Foundatio.Jobs;
6 | using Foundatio.Lock;
7 | using Microsoft.Extensions.Logging;
8 |
9 | namespace Foundatio.Tests.Jobs;
10 |
11 | public class ThrottledJob : JobWithLockBase
12 | {
13 | public ThrottledJob(ICacheClient client, ILoggerFactory loggerFactory = null) : base(loggerFactory)
14 | {
15 | _locker = new ThrottlingLockProvider(client, 1, TimeSpan.FromMilliseconds(100), null, loggerFactory);
16 | }
17 |
18 | private readonly ILockProvider _locker;
19 | public int RunCount { get; set; }
20 |
21 | protected override Task GetLockAsync(CancellationToken cancellationToken = default)
22 | {
23 | return _locker.AcquireAsync(nameof(ThrottledJob), acquireTimeout: TimeSpan.Zero);
24 | }
25 |
26 | protected override Task RunInternalAsync(JobContext context)
27 | {
28 | RunCount++;
29 | _logger.LogDebug("Incremented Run Count: {RunCount}", RunCount);
30 | return Task.FromResult(JobResult.Success);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Jobs/WithDependencyJob.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Foundatio.Jobs;
3 | using Microsoft.Extensions.Logging;
4 |
5 | namespace Foundatio.Tests.Jobs;
6 |
7 | public class WithDependencyJob : JobBase
8 | {
9 | public WithDependencyJob(MyDependency dependency, ILoggerFactory loggerFactory = null) : base(null, loggerFactory)
10 | {
11 | Dependency = dependency;
12 | }
13 |
14 | public MyDependency Dependency { get; private set; }
15 |
16 | public int RunCount { get; set; }
17 |
18 | protected override Task RunInternalAsync(JobContext context)
19 | {
20 | RunCount++;
21 |
22 | return Task.FromResult(JobResult.Success);
23 | }
24 | }
25 |
26 | public class MyDependency
27 | {
28 | public int MyProperty { get; set; }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Jobs/WithLockingJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using Foundatio.Caching;
5 | using Foundatio.Jobs;
6 | using Foundatio.Lock;
7 | using Foundatio.Messaging;
8 | using Microsoft.Extensions.Logging;
9 | using Xunit;
10 |
11 | namespace Foundatio.Tests.Jobs;
12 |
13 | public class WithLockingJob : JobWithLockBase
14 | {
15 | private readonly ILockProvider _locker;
16 |
17 | public WithLockingJob(ILoggerFactory loggerFactory) : base(loggerFactory)
18 | {
19 | _locker = new CacheLockProvider(new InMemoryCacheClient(o => o.LoggerFactory(loggerFactory)), new InMemoryMessageBus(o => o.LoggerFactory(loggerFactory)), null, loggerFactory);
20 | }
21 |
22 | public int RunCount { get; set; }
23 |
24 | protected override Task GetLockAsync(CancellationToken cancellationToken = default(CancellationToken))
25 | {
26 | return _locker.AcquireAsync(nameof(WithLockingJob), TimeSpan.FromSeconds(1), TimeSpan.Zero);
27 | }
28 |
29 | protected override async Task RunInternalAsync(JobContext context)
30 | {
31 | RunCount++;
32 |
33 | await Task.Delay(150, context.CancellationToken);
34 | Assert.True(await _locker.IsLockedAsync("WithLockingJob"));
35 |
36 | return JobResult.Success;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Messaging/Samples.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Foundatio.Tests.Messaging;
3 | using Foundatio.Utility;
4 |
5 | namespace Foundatio.Tests.Messaging
6 | {
7 | public class SimpleMessageA : ISimpleMessage
8 | {
9 | public SimpleMessageA()
10 | {
11 | Items = new DataDictionary();
12 | }
13 | public string Data { get; set; }
14 | public int Count { get; set; }
15 |
16 | public IDictionary Items { get; set; }
17 | }
18 |
19 | public class DerivedSimpleMessageA : SimpleMessageA { }
20 | public class Derived2SimpleMessageA : SimpleMessageA { }
21 | public class Derived3SimpleMessageA : SimpleMessageA { }
22 | public class Derived4SimpleMessageA : SimpleMessageA { }
23 | public class Derived5SimpleMessageA : SimpleMessageA { }
24 | public class Derived6SimpleMessageA : SimpleMessageA { }
25 | public class Derived7SimpleMessageA : SimpleMessageA { }
26 | public class Derived8SimpleMessageA : SimpleMessageA { }
27 | public class Derived9SimpleMessageA : SimpleMessageA { }
28 | public class Derived10SimpleMessageA : SimpleMessageA { }
29 | public class NeverPublishedMessage { }
30 |
31 | public class SimpleMessageB : ISimpleMessage
32 | {
33 | public string Data { get; set; }
34 | }
35 |
36 | public class SimpleMessageC
37 | {
38 | public string Data { get; set; }
39 | }
40 |
41 | public interface ISimpleMessage
42 | {
43 | string Data { get; set; }
44 | }
45 | }
46 |
47 | namespace Foundatio.Tests.MessagingAlt
48 | {
49 | public class SimpleMessageA : ISimpleMessage
50 | {
51 | public SimpleMessageA()
52 | {
53 | Items = new DataDictionary();
54 | }
55 | public string Data { get; set; }
56 | public int Count { get; set; }
57 |
58 | public IDictionary Items { get; set; }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Queue/Samples.cs:
--------------------------------------------------------------------------------
1 | using Foundatio.Metrics;
2 | using Foundatio.Queues;
3 |
4 | namespace Foundatio.Tests.Queue;
5 |
6 | public class SimpleWorkItem : IHaveSubMetricName, IHaveUniqueIdentifier
7 | {
8 | public string Data { get; set; }
9 | public int Id { get; set; }
10 | public string UniqueIdentifier { get; set; }
11 | public string SubMetricName { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Utility/BenchmarkToJson.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using BenchmarkDotNet.Exporters.Json;
4 | using BenchmarkDotNet.Loggers;
5 | using BenchmarkDotNet.Reports;
6 |
7 | namespace Foundatio.TestHarness.Utility;
8 |
9 | public class StringBenchmarkLogger : ILogger
10 | {
11 | private readonly StringBuilder _buffer = new();
12 |
13 | public string Id => Guid.NewGuid().ToString();
14 | public int Priority => 1;
15 |
16 | public void Write(LogKind logKind, string text)
17 | {
18 | _buffer.Append(text);
19 | }
20 |
21 | public void WriteLine()
22 | {
23 | _buffer.AppendLine();
24 | }
25 |
26 | public void WriteLine(LogKind logKind, string text)
27 | {
28 | _buffer.AppendLine(text);
29 | }
30 |
31 | public override string ToString()
32 | {
33 | return _buffer.ToString();
34 | }
35 |
36 | public void Flush() { }
37 | }
38 |
39 | public static class BenchmarkSummaryExtensions
40 | {
41 | public static string ToJson(this Summary summary, bool indentJson = true)
42 | {
43 | var exporter = new JsonExporter(indentJson: indentJson);
44 | var logger = new StringBenchmarkLogger();
45 | exporter.ExportToLog(summary, logger);
46 | return logger.ToString();
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Utility/Configuration.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Reflection;
3 | using Microsoft.Extensions.Configuration;
4 |
5 | namespace Foundatio.Tests.Utility;
6 |
7 | public static class Configuration
8 | {
9 | private static readonly IConfiguration _configuration;
10 | static Configuration()
11 | {
12 | _configuration = new ConfigurationBuilder()
13 | .SetBasePath(GetBasePath())
14 | .AddJsonFile("appsettings.json", true, true)
15 | .AddEnvironmentVariables()
16 | .Build();
17 | }
18 |
19 | public static IConfigurationSection GetSection(string name)
20 | {
21 | return _configuration.GetSection(name);
22 | }
23 |
24 | public static string GetConnectionString(string name)
25 | {
26 | return _configuration.GetConnectionString(name);
27 | }
28 |
29 | private static string GetBasePath()
30 | {
31 | string basePath = Path.GetDirectoryName(typeof(Configuration).GetTypeInfo().Assembly.Location);
32 |
33 | for (int i = 0; i < 5; i++)
34 | {
35 | if (File.Exists(Path.Combine(basePath, "appsettings.json")))
36 | return Path.GetFullPath(basePath);
37 |
38 | basePath += "..\\";
39 | }
40 |
41 | return Path.GetFullPath(".");
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/Foundatio.TestHarness/Utility/NonSeekableStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace Foundatio.Tests.Utility;
5 |
6 | public class NonSeekableStream : Stream
7 | {
8 | private readonly Stream _stream;
9 |
10 | public NonSeekableStream(Stream stream)
11 | {
12 | _stream = stream;
13 | }
14 |
15 | public override bool CanRead => _stream.CanRead;
16 |
17 | public override bool CanSeek => false;
18 |
19 | public override bool CanWrite => _stream.CanWrite;
20 |
21 | public override void Flush()
22 | {
23 | _stream.Flush();
24 | }
25 |
26 | public override long Length => throw new NotSupportedException();
27 |
28 | public override long Position
29 | {
30 | get => _stream.Position;
31 | set => throw new NotSupportedException();
32 | }
33 |
34 | public override int Read(byte[] buffer, int offset, int count)
35 | {
36 | return _stream.Read(buffer, offset, count);
37 | }
38 |
39 | public override long Seek(long offset, SeekOrigin origin)
40 | {
41 | throw new NotImplementedException();
42 | }
43 |
44 | public override void SetLength(long value)
45 | {
46 | throw new NotSupportedException();
47 | }
48 |
49 | public override void Write(byte[] buffer, int offset, int count)
50 | {
51 | _stream.Write(buffer, offset, count);
52 | }
53 |
54 | public override void Close()
55 | {
56 | _stream.Close();
57 | base.Close();
58 | }
59 |
60 | protected override void Dispose(bool disposing)
61 | {
62 | _stream.Dispose();
63 | base.Dispose(disposing);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Foundatio.Utf8Json/Foundatio.Utf8Json.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(PackageTags);Utf8Json;Serializer
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/Foundatio.Utf8Json/Utf8JsonSerializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Utf8Json;
4 | using Utf8Json.Resolvers;
5 |
6 | namespace Foundatio.Serializer;
7 |
8 | public class Utf8JsonSerializer : ITextSerializer
9 | {
10 | private readonly IJsonFormatterResolver _formatterResolver;
11 |
12 | public Utf8JsonSerializer(IJsonFormatterResolver resolver = null)
13 | {
14 | _formatterResolver = resolver ?? StandardResolver.Default;
15 | }
16 |
17 | public void Serialize(object data, Stream output)
18 | {
19 | JsonSerializer.NonGeneric.Serialize(data.GetType(), output, data, _formatterResolver);
20 | }
21 |
22 | public object Deserialize(Stream input, Type objectType)
23 | {
24 | return JsonSerializer.NonGeneric.Deserialize(objectType, input, _formatterResolver);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Foundatio.Xunit/Foundatio.Xunit.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(PackageTags);Logging;Log;Xunit;Retry
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/Foundatio.Xunit/Logging/LogEntry.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.Extensions.Logging;
4 |
5 | namespace Foundatio.Xunit;
6 |
7 | public class LogEntry
8 | {
9 | public DateTimeOffset Date { get; set; }
10 | public string CategoryName { get; set; }
11 | public LogLevel LogLevel { get; set; }
12 | public object[] Scopes { get; set; }
13 | public EventId EventId { get; set; }
14 | public object State { get; set; }
15 | public Exception Exception { get; set; }
16 | public Func