├── .editorconfig ├── .gitattributes ├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml └── workflows │ ├── ci.yml │ ├── pull_request.yml │ └── release.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── SimpleHosting.sln ├── coverlet.runsettings ├── global.json ├── icon.jpg └── src ├── WorldDomination.SimpleHosting.SampleHostedServiceApplication ├── CustomHostedService.cs ├── HostedService1.cs ├── HostedService2.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── WorldDomination.SimpleHosting.SampleHostedServiceApplication.csproj ├── appsettings.Development.json └── appsettings.json ├── WorldDomination.SimpleHosting.SampleWebApplication.Tests ├── CustomWebApplicationFactory.cs ├── WeatherForecastControllerTests │ └── GetTests.cs └── WorldDomination.SimpleHosting.SampleWebApplication.Tests.csproj ├── WorldDomination.SimpleHosting.SampleWebApplication ├── Controllers │ └── WeatherForecastController.cs ├── CustomHostedService.cs ├── HostedBackgroundService.cs ├── HostedService1.cs ├── HostedService2.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Services │ ├── IWeatherService.cs │ └── WeatherService.cs ├── Startup.cs ├── WeatherForecast.cs ├── WorldDomination.SimpleHosting.SampleWebApplication.csproj ├── appsettings.Development.json └── appsettings.json └── WorldDomination.SimpleHosting ├── MainOptions.cs ├── Program.cs ├── Properties └── launchSettings.json └── WorldDomination.SimpleHosting.csproj /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: 2 | http://EditorConfig.org 3 | 4 | 5 | #### How to manually clean up code in an IDE? 6 | 7 | #### - Visual Studio: CTRL + K + D 8 | #### - VSCode: SHIFT + ALT + F 9 | 10 | 11 | 12 | #################################################################################################### 13 | 14 | ### VS Settings Reference: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference 15 | 16 | 17 | 18 | 19 | # top-most EditorConfig file 20 | root = true 21 | 22 | # Default settings: 23 | # A newline ending every file 24 | # Use 4 spaces as indentation 25 | [*] 26 | insert_final_newline = true 27 | indent_style = space 28 | indent_size = 4 29 | end_of_line = crlf 30 | charset = utf-8 31 | trim_trailing_whitespace = true 32 | 33 | # C# files 34 | [*.cs] 35 | # New line preferences 36 | csharp_new_line_before_open_brace = all 37 | csharp_new_line_before_else = true 38 | csharp_new_line_before_catch = true 39 | csharp_new_line_before_finally = true 40 | csharp_new_line_before_members_in_object_initializers = true 41 | csharp_new_line_before_members_in_anonymous_types = true 42 | csharp_new_line_between_query_expression_clauses = true 43 | 44 | # Indentation preferences 45 | csharp_indent_block_contents = true 46 | csharp_indent_braces = false 47 | csharp_indent_case_contents = true 48 | csharp_indent_switch_labels = true 49 | csharp_indent_labels = one_less_than_current 50 | 51 | # avoid this. unless absolutely necessary 52 | dotnet_style_qualification_for_field = false:suggestion 53 | dotnet_style_qualification_for_property = false:suggestion 54 | dotnet_style_qualification_for_method = false:suggestion 55 | dotnet_style_qualification_for_event = false:suggestion 56 | 57 | # only use var when it's obvious what the variable type is 58 | csharp_style_var_for_built_in_types = true:suggestion 59 | csharp_style_var_when_type_is_apparent = true:suggestion 60 | csharp_style_var_elsewhere = true:suggestion 61 | 62 | # use language keywords instead of BCL types 63 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 64 | dotnet_style_predefined_type_for_member_access = true:suggestion 65 | 66 | # name all constant fields using PascalCase 67 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion 68 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields 69 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style 70 | 71 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 72 | dotnet_naming_symbols.constant_fields.required_modifiers = const 73 | 74 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 75 | 76 | # internal and private fields should be _camelCase 77 | dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion 78 | dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields 79 | dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style 80 | 81 | dotnet_naming_symbols.private_internal_fields.applicable_kinds = field 82 | dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal 83 | 84 | dotnet_naming_style.camel_case_underscore_style.required_prefix = _ 85 | dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case 86 | 87 | # Code style defaults 88 | dotnet_sort_system_directives_first = true 89 | csharp_preserve_single_line_blocks = true 90 | csharp_preserve_single_line_statements = false 91 | 92 | # Expression-level preferences 93 | dotnet_style_object_initializer = true:suggestion 94 | dotnet_style_collection_initializer = true:suggestion 95 | dotnet_style_explicit_tuple_names = true:suggestion 96 | dotnet_style_coalesce_expression = true:suggestion 97 | dotnet_style_null_propagation = true:suggestion 98 | 99 | # Expression-bodied members 100 | csharp_style_expression_bodied_methods = false:none 101 | csharp_style_expression_bodied_constructors = false:none 102 | csharp_style_expression_bodied_operators = false:none 103 | csharp_style_expression_bodied_properties = true:none 104 | csharp_style_expression_bodied_indexers = true:none 105 | csharp_style_expression_bodied_accessors = true:none 106 | 107 | # Pattern matching 108 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 109 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 110 | csharp_style_inlined_variable_declaration = true:suggestion 111 | 112 | # Null checking preferences 113 | csharp_style_throw_expression = true:suggestion 114 | csharp_style_conditional_delegate_call = true:suggestion 115 | 116 | # Space preferences 117 | csharp_space_after_cast = false 118 | csharp_space_after_colon_in_inheritance_clause = true 119 | csharp_space_after_comma = true 120 | csharp_space_after_dot = false 121 | csharp_space_after_keywords_in_control_flow_statements = true 122 | csharp_space_after_semicolon_in_for_statement = true 123 | csharp_space_around_binary_operators = before_and_after 124 | csharp_space_around_declaration_statements = do_not_ignore 125 | csharp_space_before_colon_in_inheritance_clause = true 126 | csharp_space_before_comma = false 127 | csharp_space_before_dot = false 128 | csharp_space_before_open_square_brackets = false 129 | csharp_space_before_semicolon_in_for_statement = false 130 | csharp_space_between_empty_square_brackets = false 131 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 132 | csharp_space_between_method_call_name_and_opening_parenthesis = false 133 | csharp_space_between_method_call_parameter_list_parentheses = false 134 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 135 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 136 | csharp_space_between_method_declaration_parameter_list_parentheses = false 137 | csharp_space_between_parentheses = false 138 | csharp_space_between_square_brackets = false 139 | 140 | [*.{asm,inc}] 141 | indent_size = 8 142 | 143 | # Xml project files 144 | [*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] 145 | indent_size = 2 146 | 147 | # Xml config files 148 | [*.{props,targets,config,nuspec}] 149 | indent_size = 2 150 | 151 | [CMakeLists.txt] 152 | indent_size = 2 153 | 154 | [*.cmd] 155 | indent_size = 2 156 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * -text -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at help@world-domination.com.au. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | One of the easiest ways to contribute is to participate in discussions and discuss issues. You can also contribute by submitting pull requests with code changes. 4 | 5 | ## Filing issues 6 | - Don't be afraid to ask any question about the project, including suggestions to change how things currently are. 7 | - Keep the conversation polite and respectful. This way, all parties will take an interest in your question and will be more proactive into helping. 8 | - For bugs, the best way to get your bug fixed is to be as detailed as you can be about the problem. Providing a minimal project with steps to reproduce the problem is ideal. Even though this might be frustrating, it can speed up the resolution to the problem. 9 | 10 | Helpful tip: GitHub supports [markdown](https://help.github.com/articles/github-flavored-markdown/), so when filing bugs make sure you check the formatting before clicking submit. 11 | 12 | ## Contributing code and content 13 | 14 | **Identifying the scale** 15 | 16 | If you would like to contribute to the project, first identify the "scale" of what you would like to contribute. If it is small (grammar/spelling or a bug fix) feel free to start working on a fix. If you are submitting a feature or substantial code contribution, please discuss it with the team, via an Issue. 17 | 18 | You might also read these two blogs posts on contributing code: [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza and [Don't "Push" Your Pull Requests](https://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik. Note that all code submissions will be reviewed and tested by team members. Of course (where appropriate), tests will be required. 19 | 20 | **Obtaining the source code** 21 | 22 | If you are an outside contributer, please fork the repository to your own GitHub account. See the GitHub documentation for [forking a repo](https://help.github.com/articles/fork-a-repo/) if you have any questions about this. 23 | 24 | **Submitting a Pull Request** 25 | 26 | If you don't know what a Pull Request is read then please read the article [Using Pull Requests](https://help.github.com/articles/using-pull-requests) to get up-to-speed. Make sure the respository can build and all tests pass. Familiarize yourself with the project workflow and our coding conventions. 27 | 28 | Pull Requests should all be done to the `master` branch. 29 | 30 | **Commit/Pull Request Format** 31 | 32 | ``` 33 | Summary of the changes (Less than 80 chars) 34 | - Detail 1 35 | - Detail 2 36 | 37 | Addresses #bugnumber (in this specific format) 38 | ``` 39 | 40 | **Tests** 41 | 42 | - Tests need to be provided for every bug/feature that is completed. 43 | - Tests only need to be present for issues that need to be verified by QA (e.g. not tasks) 44 | - If there is a scenario that is far _too hard*_ to test there does not need to be a test for it. 45 | 46 | 47 | *"_Too hard_" is determined by the team as a whole. 48 | 49 | --- 50 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: PureKrome 2 | custom: ['https://paypal.me/purekrome'] 3 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | paths-ignore: 7 | - 'ReadMe.md' 8 | 9 | permissions: 10 | packages: write 11 | 12 | env: 13 | DOTNET_NOLOGO: true 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v3 22 | 23 | - name: Build version suffix 24 | run: echo "VERSION_SUFFIX=beta.${{ github.run_number }}" >> $GITHUB_ENV 25 | 26 | - name: Setup .NET 27 | uses: actions/setup-dotnet@v3 28 | 29 | - run: dotnet restore --verbosity minimal 30 | 31 | - run: dotnet build --configuration Release /p:ContinuousIntegrationBuild=true 32 | 33 | - run: dotnet test --configuration Release --no-build 34 | 35 | - run: dotnet pack --configuration Release --no-build --output ./artifacts --version-suffix $VERSION_SUFFIX 36 | 37 | - name: Publish artifacts 38 | uses: actions/upload-artifact@v3 39 | with: 40 | name: WorldDomination.SimpleHosting.1.0.0-${{ env.VERSION_SUFFIX }} 41 | path: ./artifacts/ 42 | 43 | - name: List contents of the Artifacts directory 44 | run: ls -al ./artifacts 45 | 46 | - name: Publish to GPR 47 | run: | 48 | dotnet nuget push "./artifacts/*.nupkg" \ 49 | --no-symbols \ 50 | --api-key ${{ secrets.GITHUB_TOKEN }} \ 51 | --source https://nuget.pkg.github.com/${{ github.repository_owner }} 52 | 53 | test: 54 | runs-on: ubuntu-latest 55 | 56 | steps: 57 | - name: Checkout repo 58 | uses: actions/checkout@v3 59 | 60 | - name: Setup .NET 61 | uses: actions/setup-dotnet@v3 62 | 63 | - run: dotnet restore --verbosity minimal 64 | 65 | - run: dotnet build --configuration Debug 66 | 67 | - run: dotnet test --configuration Debug --verbosity minimal --no-build --collect:"XPlat Code Coverage" --results-directory "./.codecoverage" 68 | 69 | - name: Code coverage 70 | uses: codecov/codecov-action@v3 71 | with: 72 | token: "${{ secrets.CODECOV_TOKEN }}" 73 | directory: "./.codecoverage" 74 | fail_ci_if_error: true 75 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | paths-ignore: 6 | - 'ReadMe.md' 7 | 8 | env: 9 | DOTNET_NOLOGO: true 10 | 11 | jobs: 12 | build: 13 | runs-on: ubuntu-latest 14 | 15 | strategy: 16 | matrix: 17 | config: 18 | - debug 19 | - release 20 | 21 | steps: 22 | - name: Checkout repository 23 | uses: actions/checkout@v3 24 | 25 | - name: Build version suffix 26 | run: echo "VERSION_SUFFIX=alpha.${{ github.event.number }}" >> $GITHUB_ENV 27 | 28 | - name: Setup .NET 29 | uses: actions/setup-dotnet@v3 30 | 31 | - run: dotnet restore --verbosity minimal 32 | 33 | - run: dotnet build --configuration ${{ matrix.config }} /p:ContinuousIntegrationBuild=true 34 | 35 | - run: dotnet test --configuration ${{ matrix.config }} --no-build 36 | if: matrix.config == 'release' 37 | 38 | - run: dotnet test --configuration ${{ matrix.config }} --verbosity minimal --no-build --collect:"XPlat Code Coverage" --results-directory "./.codecoverage" 39 | if: matrix.config == 'debug' 40 | 41 | - run: dotnet pack --configuration ${{ matrix.config }} --no-build --output ./artifacts --version-suffix $VERSION_SUFFIX 42 | if: matrix.config == 'release' 43 | 44 | - name: Code coverage 45 | if: matrix.config == 'debug' 46 | uses: codecov/codecov-action@v3 47 | with: 48 | directory: "./.codecoverage" 49 | fail_ci_if_error: true 50 | 51 | - name: Publish artifacts 52 | if: matrix.config == 'release' 53 | uses: actions/upload-artifact@v3 54 | with: 55 | name: WorldDomination.SimpleHosting.1.0.0-${{ env.VERSION_SUFFIX }} 56 | path: ./artifacts/ 57 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | - "*.*.*" 8 | 9 | permissions: 10 | contents: write 11 | packages: write 12 | 13 | env: 14 | DOTNET_NOLOGO: true 15 | 16 | jobs: 17 | build: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | 22 | - name: Calculate version from the Commit Tag 23 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 24 | 25 | - name: Checkout repository 26 | uses: actions/checkout@v3 27 | 28 | - name: Setup .NET 29 | uses: actions/setup-dotnet@v3 30 | 31 | - run: dotnet restore --verbosity minimal 32 | 33 | - run: dotnet build --configuration Release /p:ContinuousIntegrationBuild=true /p:version=${{ env.RELEASE_VERSION }} 34 | 35 | - run: dotnet pack --configuration Release --no-build --output ./artifacts /p:version=${{ env.RELEASE_VERSION }} 36 | 37 | - name: Publish artifacts 38 | uses: actions/upload-artifact@v3 39 | with: 40 | name: WorldDomination.SimpleHosting.${{ env.RELEASE_VERSION }} 41 | path: ./artifacts/ 42 | 43 | - name: Upload release assets 44 | uses: softprops/action-gh-release@v1 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 47 | with: 48 | files: ./artifacts/* 49 | 50 | - name: Publish to GPR 51 | run: | 52 | dotnet nuget push "./artifacts/*.nupkg" \ 53 | --no-symbols \ 54 | --api-key ${{ secrets.GITHUB_TOKEN }} \ 55 | --source https://nuget.pkg.github.com/${{ github.repository_owner }} 56 | 57 | - name: Publish to nuget.org 58 | run: | 59 | dotnet nuget push "./artifacts/*.nupkg" \ 60 | --api-key ${{ secrets.NUGET_TOKEN }} \ 61 | --source https://api.nuget.org/v3/index.json 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.obj 2 | *.pdb 3 | *.user 4 | *.aps 5 | *.pch 6 | *.vspscc 7 | *.vssscc 8 | *_i.c 9 | *_p.c 10 | *.ncb 11 | *.suo 12 | *.tlb 13 | *.tlh 14 | *.bak 15 | *.cache 16 | *.ilk 17 | *.log 18 | *.lib 19 | *.sbr 20 | *.scc 21 | [Bb]in 22 | [Dd]ebug*/ 23 | obj/ 24 | [Rr]elease*/ 25 | _ReSharper*/ 26 | *.[Pp]ublish.xml 27 | *.resharper* 28 | AppData/ 29 | App_Data/ 30 | *.log.* 31 | [Ll]ogs/ 32 | [Dd]ata/ 33 | [Pp]ackages/ 34 | [Tt]humbs.db 35 | [Tt]est[Rr]esult* 36 | [Bb]uild[Ll]og.* 37 | *.sln.DotSettings.* 38 | *.ncrunchproject 39 | *.ncrunchsolution 40 | *.nupkg 41 | *.vs 42 | *.vscode -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018-2019 Homely.com.au (Original code I did, based from that) 4 | 5 | Copyright (c) 2020 Pure Krome 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Simple: Hosting

2 | 3 |
4 | Making it simple to customize Hosting for your ASP.NET Core 6.x+ application 5 |
6 | 7 |
8 | 9 |
10 | 11 | 12 | License - MIT 13 | 14 | 15 | 16 | NuGet 17 | 18 |
19 | 20 | ## Key Points 21 | 22 | - :rocket: Reduces boilerplate ceremony for your `program.cs` file. 23 | - :white_check_mark: Sets up Serilog _around_ the entire application. (:wrench: Configure all settings via your `appsettings.json` file(s)) 24 | - :white_check_mark: Simple to add some extra (helpful) log header/footer. 25 | - :white_check_mark: Can also add logging to your `Startup` class (new with ASP.NET Core 6+) 26 | 27 | In summary: this library makes is SIMPLE (by abstracting away most of the boring ceremony) to setup your ASP.NET Core application. 28 | 29 | --- 30 | ## Installation 31 | 32 | Package is available via NuGet. 33 | 34 | ```sh 35 | dotnet add package WorldDomination.SimpleHosting 36 | ``` 37 | 38 | --- 39 | ## More Information: 40 | 41 | Basically, turn your `program.cs` into this : 42 | 43 | ``` 44 | public static Task Main(string[] args) 45 | { 46 | return WorldDomination.SimpleHosting.Program.Main(args); 47 | } 48 | ``` 49 | 50 | and you've now got some Serilog all wired up along with some nice application-wide error handling. 51 | 52 | For more custom settings: 53 | 54 | ``` 55 | public static Task Main(string[] args) 56 | { 57 | // Every Option here is optional. 58 | // Set what you want. 59 | var options = new MainOptions 60 | { 61 | CommandLineArguments = args, 62 | FirstLoggingInformationMessage = "~~ Sample Web Application ~~", 63 | LogAssemblyInformation = true, 64 | LastLoggingInformationMessage = "-- Sample Web Application has ended/terminated --", 65 | StartupActivation = new System.Func((context, logger) => new Startup(context.Configuration, logger)) 66 | }; 67 | 68 | return SimpleHosting.Program.Main(options); 69 | } 70 | ``` 71 | 72 | --- 73 | 74 | ## Contribute 75 | Yep - contributions are always welcome. Please read the contribution guidelines first. 76 | 77 | ## Code of Conduct 78 | 79 | If you wish to participate in this repository then you need to abide by the code of conduct. 80 | 81 | ## Feedback 82 | 83 | Yes! Please use the Issues section to provide feedback - either good or needs improvement :cool: 84 | 85 | --- 86 | 87 | -------------------------------------------------------------------------------- /SimpleHosting.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldDomination.SimpleHosting", "src\WorldDomination.SimpleHosting\WorldDomination.SimpleHosting.csproj", "{112DA850-B52E-49B6-811D-73237C5A7792}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldDomination.SimpleHosting.SampleWebApplication", "src\WorldDomination.SimpleHosting.SampleWebApplication\WorldDomination.SimpleHosting.SampleWebApplication.csproj", "{013F5235-D334-4770-82E4-350BA00F676C}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldDomination.SimpleHosting.SampleWebApplication.Tests", "src\WorldDomination.SimpleHosting.SampleWebApplication.Tests\WorldDomination.SimpleHosting.SampleWebApplication.Tests.csproj", "{BB527810-A846-43A8-8AD8-A1C2B191C2AE}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{3F8381EE-2CFD-4533-8141-5C6EA41E3275}" 13 | ProjectSection(SolutionItems) = preProject 14 | .editorconfig = .editorconfig 15 | global.json = global.json 16 | README.md = README.md 17 | EndProjectSection 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B77C557B-74CB-457F-AB66-B0BBCF93FA5C}" 20 | EndProject 21 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorldDomination.SimpleHosting.SampleHostedServiceApplication", "src\WorldDomination.SimpleHosting.SampleHostedServiceApplication\WorldDomination.SimpleHosting.SampleHostedServiceApplication.csproj", "{F8A89E7A-90A2-454C-9982-67B359A55E22}" 22 | EndProject 23 | Global 24 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 25 | Debug|Any CPU = Debug|Any CPU 26 | Release|Any CPU = Release|Any CPU 27 | EndGlobalSection 28 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 29 | {112DA850-B52E-49B6-811D-73237C5A7792}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {112DA850-B52E-49B6-811D-73237C5A7792}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {112DA850-B52E-49B6-811D-73237C5A7792}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {112DA850-B52E-49B6-811D-73237C5A7792}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {013F5235-D334-4770-82E4-350BA00F676C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {013F5235-D334-4770-82E4-350BA00F676C}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {013F5235-D334-4770-82E4-350BA00F676C}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {013F5235-D334-4770-82E4-350BA00F676C}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {BB527810-A846-43A8-8AD8-A1C2B191C2AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 38 | {BB527810-A846-43A8-8AD8-A1C2B191C2AE}.Debug|Any CPU.Build.0 = Debug|Any CPU 39 | {BB527810-A846-43A8-8AD8-A1C2B191C2AE}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | {BB527810-A846-43A8-8AD8-A1C2B191C2AE}.Release|Any CPU.Build.0 = Release|Any CPU 41 | {F8A89E7A-90A2-454C-9982-67B359A55E22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 42 | {F8A89E7A-90A2-454C-9982-67B359A55E22}.Debug|Any CPU.Build.0 = Debug|Any CPU 43 | {F8A89E7A-90A2-454C-9982-67B359A55E22}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {F8A89E7A-90A2-454C-9982-67B359A55E22}.Release|Any CPU.Build.0 = Release|Any CPU 45 | EndGlobalSection 46 | GlobalSection(SolutionProperties) = preSolution 47 | HideSolutionNode = FALSE 48 | EndGlobalSection 49 | GlobalSection(NestedProjects) = preSolution 50 | {112DA850-B52E-49B6-811D-73237C5A7792} = {B77C557B-74CB-457F-AB66-B0BBCF93FA5C} 51 | {013F5235-D334-4770-82E4-350BA00F676C} = {B77C557B-74CB-457F-AB66-B0BBCF93FA5C} 52 | {BB527810-A846-43A8-8AD8-A1C2B191C2AE} = {B77C557B-74CB-457F-AB66-B0BBCF93FA5C} 53 | {F8A89E7A-90A2-454C-9982-67B359A55E22} = {B77C557B-74CB-457F-AB66-B0BBCF93FA5C} 54 | EndGlobalSection 55 | GlobalSection(ExtensibilityGlobals) = postSolution 56 | SolutionGuid = {D2610FC5-400C-462E-8BEC-028ECC97A1EF} 57 | EndGlobalSection 58 | EndGlobal 59 | -------------------------------------------------------------------------------- /coverlet.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | cobertura 8 | [Sample*]* 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "6.0.100", 4 | "rollForward": "latestFeature" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PureKrome/SimpleHosting/5eac0ab1902b096d9378fa67c2022dce4a5d8a70/icon.jpg -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/CustomHostedService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace WorldDomination.SimpleHosting.SampleHostedServiceApplication 8 | { 9 | public abstract class CustomHostedService : IHostedService 10 | { 11 | private readonly string _name; 12 | private readonly ILogger _logger; 13 | 14 | public CustomHostedService(string name, ILogger logger) 15 | { 16 | _name = name; 17 | _logger = logger; 18 | } 19 | 20 | public async Task StartAsync(CancellationToken cancellationToken) 21 | { 22 | _logger.LogInformation("Starting executing {name} worker.", _name); 23 | 24 | // Just delay for a bit .. then finish. 25 | // E.g. Doing some Database preparation. 26 | _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now.ToString()); 27 | await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); 28 | 29 | _logger.LogInformation("Finishing StartAsync {name} worker.", _name); 30 | } 31 | 32 | public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/HostedService1.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace WorldDomination.SimpleHosting.SampleHostedServiceApplication 4 | { 5 | public class HostedService1 : CustomHostedService 6 | { 7 | public HostedService1(ILogger logger) : base("HostedService-1", logger) 8 | { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/HostedService2.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace WorldDomination.SimpleHosting.SampleHostedServiceApplication 4 | { 5 | public class HostedService2 : CustomHostedService 6 | { 7 | public HostedService2(ILogger logger) : base("HostedService-2", logger) 8 | { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | namespace WorldDomination.SimpleHosting.SampleHostedServiceApplication 7 | { 8 | public class Program 9 | { 10 | public static Task Main(string[] args) 11 | { 12 | var options = new MainOptions 13 | { 14 | CommandLineArguments = args, 15 | FirstLoggingInformationMessage = "~~ Sample Hosted Services Application ~~", 16 | LogAssemblyInformation = true, 17 | LastLoggingInformationMessage = "-- Sample Hosted Services Application has ended/terminated --", 18 | ConfigureCustomServices = new Action((hostContext, services) => 19 | { 20 | services.AddHostedService(); 21 | services.AddHostedService(); 22 | }) 23 | }; 24 | 25 | return SimpleHosting.Program.Main(options); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SampleBackgroundTask": { 4 | "commandName": "Project", 5 | "environmentVariables": { 6 | "DOTNET_ENVIRONMENT": "Development" 7 | } 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/WorldDomination.SimpleHosting.SampleHostedServiceApplication.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleHostedServiceApplication/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Serilog": { 3 | "MinimumLevel": { 4 | "Default": "Debug" 5 | }, 6 | "WriteTo": [ 7 | { 8 | "Name": "Console" 9 | } 10 | ], 11 | "Enrich": [ "FromLogContext" ], 12 | "Properties": { 13 | "ApplicationName": "SampleBackgroundTask" 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication.Tests/CustomWebApplicationFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FizzWare.NBuilder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Mvc.Testing; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Hosting; 7 | using Moq; 8 | using WorldDomination.SimpleHosting.SampleWebApplication.Services; 9 | 10 | namespace WorldDomination.SimpleHosting.SampleWebApplication.Tests 11 | { 12 | public class CustomWebApplicationFactory : WebApplicationFactory 13 | { 14 | protected override IHostBuilder CreateHostBuilder() => SimpleHosting.Program.CreateHostBuilder(new MainOptions()); 15 | 16 | public MainOptions MainOptions{ get; set; } 17 | 18 | protected override void ConfigureWebHost(IWebHostBuilder builder) 19 | { 20 | builder.ConfigureServices(services => 21 | { 22 | // Remove the existing registration for an IWeatherService. 23 | var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IWeatherService)); 24 | if (descriptor != null) 25 | { 26 | services.Remove(descriptor); 27 | } 28 | 29 | // Use a mocked IWeatherService. 30 | var weatherForcasts = Builder.CreateListOfSize(5).Build(); 31 | var weatherService = new Mock(); 32 | weatherService.Setup(x => x.GetWeatherAsync()).ReturnsAsync(weatherForcasts); 33 | services.AddTransient(_ => weatherService.Object); 34 | }); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication.Tests/WeatherForecastControllerTests/GetTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Logging; 4 | using Shouldly; 5 | using Xunit; 6 | 7 | namespace WorldDomination.SimpleHosting.SampleWebApplication.Tests.WeatherForecastControllerTests 8 | { 9 | public class GetTests : IClassFixture 10 | { 11 | private readonly CustomWebApplicationFactory _factory; 12 | 13 | public GetTests(CustomWebApplicationFactory factory) 14 | { 15 | _factory = factory; 16 | } 17 | 18 | public static TheoryData> Data => new TheoryData> 19 | { 20 | { 21 | new MainOptions() 22 | }, 23 | { 24 | new MainOptions 25 | { 26 | StartupActivation = new System.Func((context, logger) => new Startup(context.Configuration, logger)) 27 | } 28 | } 29 | }; 30 | 31 | [Theory] 32 | [MemberData(nameof(Data))] 33 | public async Task GivenAValidRequest_Get_ReturnsAnHttpStatus200OK(MainOptions mainOptions) 34 | { 35 | _factory.MainOptions = mainOptions; 36 | 37 | var client = _factory.CreateClient(); 38 | 39 | // Act. 40 | var result = await client.GetAsync("/WeatherForecast"); 41 | 42 | // Assert. 43 | result.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication.Tests/WorldDomination.SimpleHosting.SampleWebApplication.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/Controllers/WeatherForecastController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Mvc; 5 | using WorldDomination.SimpleHosting.SampleWebApplication.Services; 6 | 7 | namespace WorldDomination.SimpleHosting.SampleWebApplication.Controllers 8 | { 9 | [ApiController] 10 | [Route("[controller]")] 11 | public class WeatherForecastController : ControllerBase 12 | { 13 | private readonly IWeatherService _weatherService; 14 | 15 | public WeatherForecastController(IWeatherService weatherService) 16 | { 17 | _weatherService = weatherService ?? throw new ArgumentNullException(nameof(weatherService)); 18 | } 19 | 20 | [HttpGet] 21 | public async Task> Get() 22 | { 23 | return await _weatherService.GetWeatherAsync(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/CustomHostedService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace WorldDomination.SimpleHosting.SampleWebApplication 8 | { 9 | public abstract class CustomHostedService : IHostedService 10 | { 11 | private readonly string _name; 12 | private readonly ILogger _logger; 13 | 14 | public CustomHostedService(string name, ILogger logger) 15 | { 16 | _name = name; 17 | _logger = logger; 18 | } 19 | 20 | public async Task StartAsync(CancellationToken cancellationToken) 21 | { 22 | _logger.LogInformation($"Starting executing {_name} worker."); 23 | 24 | // Just delay for a bit .. then finish. 25 | // E.g. Doing some Database preparation. 26 | _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now.ToString()); 27 | await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); 28 | 29 | _logger.LogInformation($"Finishing StartAsync {_name} worker."); 30 | } 31 | 32 | public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/HostedBackgroundService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace WorldDomination.SimpleHosting.SampleWebApplication 8 | { 9 | public class HostedBackgroundService : BackgroundService 10 | { 11 | private readonly IServiceProvider _services; 12 | private readonly ILogger _logger; 13 | 14 | public HostedBackgroundService(IServiceProvider services, ILogger logger) 15 | { 16 | _services = services ?? throw new ArgumentNullException(nameof(services)); 17 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 18 | } 19 | 20 | protected override async Task ExecuteAsync(CancellationToken cancellationToken) 21 | { 22 | _logger.LogInformation("Consume Scoped Service Hosted Service running."); 23 | 24 | while (!cancellationToken.IsCancellationRequested) 25 | { 26 | _logger.LogDebug("Doing stuff that takes a while (like checking a queue) ..."); 27 | 28 | await Task.Delay(1000 * 5, cancellationToken); 29 | 30 | _logger.LogDebug("Doing stuff - finisihed."); 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/HostedService1.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace WorldDomination.SimpleHosting.SampleWebApplication 4 | { 5 | public class HostedService1 : CustomHostedService 6 | { 7 | public HostedService1(ILogger logger) : base("HostedService-1 (e.g. Database setup / seeding)", logger) 8 | { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/HostedService2.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace WorldDomination.SimpleHosting.SampleWebApplication 4 | { 5 | public class HostedService2 : CustomHostedService 6 | { 7 | public HostedService2(ILogger logger) : base("HostedService-2 (e.g. Database migrations)", logger) 8 | { 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace WorldDomination.SimpleHosting.SampleWebApplication 6 | { 7 | public class Program 8 | { 9 | public static Task Main(string[] args) 10 | { 11 | var options = new MainOptions 12 | { 13 | CommandLineArguments = args, 14 | FirstLoggingInformationMessage = "~~ Sample Web Application ~~", 15 | LogAssemblyInformation = true, 16 | LastLoggingInformationMessage = "-- Sample Web Application has ended/terminated --", 17 | StartupActivation = new System.Func((context, logger) => new Startup(context.Configuration, logger)) 18 | }; 19 | 20 | return SimpleHosting.Program.Main(options); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "Kestrel": { 5 | "commandName": "Project", 6 | "launchBrowser": true, 7 | "launchUrl": "weatherforecast", 8 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 9 | "environmentVariables": { 10 | "ASPNETCORE_ENVIRONMENT": "Development" 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/Services/IWeatherService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace WorldDomination.SimpleHosting.SampleWebApplication.Services 5 | { 6 | public interface IWeatherService 7 | { 8 | Task> GetWeatherAsync(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/Services/WeatherService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace WorldDomination.SimpleHosting.SampleWebApplication.Services 7 | { 8 | public class WeatherService : IWeatherService 9 | { 10 | private static readonly string[] Summaries = new[] 11 | { 12 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 13 | }; 14 | 15 | public async Task> GetWeatherAsync() 16 | { 17 | var rng = new Random(); 18 | var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast 19 | { 20 | Date = DateTime.Now.AddDays(index), 21 | TemperatureC = rng.Next(-20, 55), 22 | Summary = Summaries[rng.Next(Summaries.Length)] 23 | }) 24 | .ToArray(); 25 | 26 | return await Task.FromResult(result); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Http; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | using WorldDomination.SimpleHosting.SampleWebApplication.Services; 10 | 11 | namespace WorldDomination.SimpleHosting.SampleWebApplication 12 | { 13 | public class Startup 14 | { 15 | private readonly ILogger _logger; 16 | private readonly IConfiguration _configuration; 17 | 18 | public Startup(IConfiguration configuration) 19 | { 20 | _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); 21 | } 22 | 23 | public Startup(IConfiguration configuration, ILogger logger) 24 | { 25 | _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); 26 | _logger = logger ?? throw new ArgumentNullException(nameof(logger)); 27 | } 28 | 29 | // This method gets called by the runtime. Use this method to add services to the container. 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | if (_logger != null) 33 | { 34 | _logger.LogInformation("Configuring services"); 35 | } 36 | 37 | services.AddControllers(); 38 | 39 | // Some fake database migration service. 40 | services.AddHostedService(); 41 | services.AddHostedService(); 42 | services.AddHostedService(); 43 | 44 | // This is a -real- Weather Service. 45 | services.AddTransient(); 46 | } 47 | 48 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 49 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 50 | { 51 | if (_logger != null) 52 | { 53 | _logger.LogInformation("Configuring Middleware"); 54 | } 55 | 56 | if (env.IsDevelopment()) 57 | { 58 | app.UseDeveloperExceptionPage(); 59 | } 60 | 61 | app.UseHttpsRedirection(); 62 | 63 | app.UseRouting(); 64 | 65 | app.UseAuthorization(); 66 | 67 | app.UseEndpoints(endpoints => 68 | { 69 | // Root/default route. 70 | endpoints.MapGet("/", async context => 71 | { 72 | await context.Response.WriteAsync("Hello World!"); 73 | }); 74 | 75 | endpoints.MapControllers(); 76 | }); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/WeatherForecast.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace WorldDomination.SimpleHosting.SampleWebApplication 4 | { 5 | public class WeatherForecast 6 | { 7 | public DateTime Date { get; set; } 8 | 9 | public int TemperatureC { get; set; } 10 | 11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 12 | 13 | public string Summary { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/WorldDomination.SimpleHosting.SampleWebApplication.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Debug", 6 | "Microsoft": "Debug" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting.SampleWebApplication/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Debug" 6 | }, 7 | "WriteTo": [ 8 | { 9 | "Name": "Console" 10 | } 11 | ], 12 | "Enrich": [ "FromLogContext" ], 13 | "Properties": { 14 | "ApplicationName": "SampleWebApplication" 15 | } 16 | }, 17 | 18 | "Logging": { 19 | "LogLevel": { 20 | "Default": "Warning" 21 | } 22 | }, 23 | 24 | 25 | "AllowedHosts": "*" 26 | } 27 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting/MainOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace WorldDomination.SimpleHosting 8 | { 9 | public class MainOptions where TStartup : class 10 | { 11 | /// 12 | /// Command line arguments. 13 | /// 14 | public string[] CommandLineArguments { get; set; } 15 | 16 | /// 17 | /// Optional text which is first displayed when the application starts. 18 | /// 19 | /// This can be useful to help determine if things have started and are working ok. 20 | public string FirstLoggingInformationMessage { get; set; } 21 | 22 | /// 23 | /// Write the assembly name, version and date information to the logger? 24 | /// 25 | /// Default: true. 26 | public bool LogAssemblyInformation { get; set; } = true; 27 | 28 | /// 29 | /// Write the .NET SDK Framework to the logger? 30 | /// 31 | /// Default: true. 32 | public bool LogFrameworkInformation { get; set; } = true; 33 | 34 | /// 35 | /// Write the OS platform to the logger? 36 | /// 37 | /// Default: true. 38 | public bool LogOSDesriptionInformation { get; set; } = true; 39 | 40 | /// 41 | /// Optional text which is last displayed when the application stops. 42 | /// 43 | /// This could be useful to help determine when things are finally stopping. 44 | public string LastLoggingInformationMessage { get; set; } 45 | 46 | /// 47 | /// Adds services to the container. This can be called multiple times and the results will be additive. 48 | /// 49 | /// This is required for Background Hosted Services, where there is no ConfigureService method to override, such as with a Web host. 50 | public Action ConfigureCustomServices { get; set; } 51 | 52 | public Func StartupActivation { get; set; } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.Extensions.Logging; 10 | using Serilog; 11 | using Serilog.Core; 12 | using Serilog.Extensions.Logging; 13 | 14 | namespace WorldDomination.SimpleHosting 15 | { 16 | public class Program 17 | { 18 | private static readonly string _explosion = @"" + Environment.NewLine + 19 | "" + Environment.NewLine + 20 | "" + Environment.NewLine + 21 | " ____" + Environment.NewLine + 22 | " __,-~~/~ `---." + Environment.NewLine + 23 | " _/_,---( , )" + Environment.NewLine + 24 | " __ / < / ) \\___" + Environment.NewLine + 25 | "- ------===;;;'====------------------===;;;===----- - -" + Environment.NewLine + 26 | " \\/ ~\"~\"~\"~\"~\"~\\~\"~)~\"/" + Environment.NewLine + 27 | " (_ ( \\ ( > \\)" + Environment.NewLine + 28 | " \\_(_<> _>'" + Environment.NewLine + 29 | " ~ `-i' ::>|--\"" + Environment.NewLine + 30 | " I;|.|.|" + Environment.NewLine + 31 | " <|i::|i|`." + Environment.NewLine + 32 | " (` ^'\"`-' \")" + Environment.NewLine + 33 | "------------------------------------------------------------------" + Environment.NewLine + 34 | "[Nuclear Explosion Mushroom by Bill March]" + Environment.NewLine + 35 | "" + Environment.NewLine + 36 | "------------------------------------------------" + Environment.NewLine + 37 | ""; 38 | 39 | /// 40 | /// The program's main start/entry point. Hold on to your butts .... here we go! 41 | /// 42 | /// Startup class type. 43 | /// Optional command line arguments. 44 | /// Task of this Main application run. 45 | public static async Task Main(string[] args) where TStartup : class 46 | { 47 | var options = new MainOptions 48 | { 49 | CommandLineArguments = args 50 | }; 51 | 52 | await Main(options); 53 | } 54 | 55 | /// 56 | /// The program's main start/entry point. Hold on to your butts .... here we go! 57 | /// 58 | /// Startup class type. 59 | /// Options to help setup/configure your program. 60 | /// Task of this Main application run. 61 | public static async Task Main(MainOptions options) where TStartup : class 62 | { 63 | try 64 | { 65 | if (options is null) 66 | { 67 | throw new ArgumentNullException(nameof(options)); 68 | } 69 | 70 | // Before we do _ANYTHING_ we need to have a logger so we can start 71 | // seeing what is going on ... good or bad. 72 | Log.Logger = new LoggerConfiguration() 73 | .ReadFrom.Configuration(GetConfigurationBuilder()) 74 | .Enrich.FromLogContext() 75 | .CreateLogger(); 76 | 77 | // Display any (optional) initial banner / opening text to define the start of this application now starting. 78 | if (!string.IsNullOrWhiteSpace(options.FirstLoggingInformationMessage)) 79 | { 80 | Log.Information(options.FirstLoggingInformationMessage); 81 | } 82 | 83 | if (options.LogAssemblyInformation) 84 | { 85 | var assembly = typeof(TStartup).Assembly; 86 | var assemblyDate = string.IsNullOrWhiteSpace(assembly.Location) 87 | ? "-- unknown --" 88 | : File.GetLastWriteTime(assembly.Location).ToString("u"); 89 | 90 | var assemblyInfo = $"Name: {assembly.GetName().Name} | Version: {assembly.GetName().Version} | Date: {assemblyDate}"; 91 | 92 | Log.Information(assemblyInfo); 93 | } 94 | 95 | if (options.LogFrameworkInformation) 96 | { 97 | Log.Information($"Framework: {RuntimeInformation.FrameworkDescription}"); 98 | } 99 | 100 | if (options.LogOSDesriptionInformation) 101 | { 102 | Log.Information($"OS Platform: {RuntimeInformation.OSDescription}"); 103 | } 104 | 105 | var host = CreateHostBuilder(options).Build(); 106 | 107 | // Ok, now lets go and start! 108 | await host.RunAsync(); 109 | } 110 | catch (Exception exception) 111 | { 112 | const string errorMessage = "Something seriously unexpected has occurred while preparing the Host. Sadness :~("; 113 | 114 | // We might NOT have created a logger ... because we might be _trying_ to create the logger but 115 | // we have some bad setup-configuration-data and boom!!! No logger successfully setup/created. 116 | // So, if we do have a logger created, then use it. 117 | if (Log.Logger is Logger) 118 | { 119 | // TODO: Add metrics (like Application Insights?) to log telemetry failures -IF- Serilog can't do this adequately. 120 | Log.Logger.Fatal(exception, errorMessage); 121 | } 122 | else 123 | { 124 | // Nope - failed to create a logger and we have a serious error. So lets 125 | // just fall back to the Console and _hope_ someone can read/access that. 126 | Console.WriteLine(_explosion); 127 | Console.WriteLine(errorMessage); 128 | Console.WriteLine(); 129 | Console.WriteLine(); 130 | Console.WriteLine($"Error: {exception.Message}"); 131 | Console.WriteLine(); 132 | } 133 | } 134 | finally 135 | { 136 | var shutdownMessage = string.IsNullOrWhiteSpace(options.LastLoggingInformationMessage) 137 | ? "Application has now shutdown." 138 | : options.LastLoggingInformationMessage; 139 | 140 | // Again: did we successfully create a logger? 141 | if (Log.Logger is Logger) 142 | { 143 | Log.Information(shutdownMessage); 144 | 145 | // Ensure to flush and stop internal timers/threads before application-exit (Avoid segmentation fault on Linux) 146 | Log.CloseAndFlush(); 147 | } 148 | else 149 | { 150 | Console.WriteLine(shutdownMessage); 151 | } 152 | } 153 | } 154 | 155 | // This is only to help load the SERILOG information. 156 | // - appsettings.json 157 | // - appsettings..json 158 | // - environmental variables 159 | // Strongly based off/from: https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.hosting.host.createdefaultbuilder?view=dotnet-plat-ext-5.0 160 | private static IConfiguration GetConfigurationBuilder() 161 | { 162 | var builder = new ConfigurationBuilder() 163 | .SetBasePath(Directory.GetCurrentDirectory()) 164 | .AddJsonFile("appsettings.json", optional: false); 165 | 166 | // Check any 'Environment' json files, like appsettings.Development.json. 167 | // REF: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?#environmentname 168 | var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? 169 | Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? 170 | "Production"; 171 | 172 | return builder 173 | .AddJsonFile($"appsettings.{environment}.json", optional: true) 174 | .AddEnvironmentVariables() 175 | .Build(); 176 | } 177 | 178 | public static IHostBuilder CreateHostBuilder(MainOptions options) where TStartup : class 179 | { 180 | var hostBuilder = Host 181 | .CreateDefaultBuilder(options.CommandLineArguments) 182 | .ConfigureLogging((context, logging) => 183 | { 184 | // Don't want any of the default crap. 185 | logging.ClearProviders(); 186 | }) 187 | .UseSerilog(); 188 | 189 | if (options.ConfigureCustomServices != null) 190 | { 191 | hostBuilder.ConfigureServices(options.ConfigureCustomServices); 192 | } 193 | 194 | var logger = new SerilogLoggerProvider(Log.Logger).CreateLogger(nameof(Program)); 195 | 196 | hostBuilder 197 | .ConfigureWebHostDefaults(webBuilder => 198 | { 199 | if (options.StartupActivation is null) 200 | { 201 | // Normal startup class with default constructor. 202 | webBuilder.UseStartup(); 203 | } 204 | else 205 | { 206 | // Use the custom startup activation function, instead. 207 | // REF: https://docs.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-5.0?view=aspnetcore-5.0#control-startup-class-activation 208 | // (Pro Tip: this is a great way to add logging, to Startup.cs !!! YES!!!! ) 209 | //webBuilder.UseStartup(c => new TStartup(c)); 210 | webBuilder.UseStartup(context => options.StartupActivation(context, logger)); 211 | 212 | // The startup class (activated, above) will be activated in _this_ assmebly and not the main host/app assembly. 213 | // This means that when things like 'MapControllers' tries to do an assembly scan (the default functionality) 214 | // in the host/app assembly, it will FAIL to find any. 215 | // As such, we actually need to really reset the main ApplicationKey to say it's for the provided startup class. 216 | // Hat tip to: @aarondandy, @buildstarted and @xt0rted 217 | var startupAssemblyName = options.StartupActivation.GetMethodInfo().DeclaringType!.GetTypeInfo().Assembly.GetName().Name; 218 | webBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName); 219 | } 220 | }); 221 | 222 | return hostBuilder; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:54134/", 7 | "sslPort": 44317 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "SimpleHosting": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "environmentVariables": { 22 | "ASPNETCORE_ENVIRONMENT": "Development" 23 | }, 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000" 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/WorldDomination.SimpleHosting/WorldDomination.SimpleHosting.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | latest 6 | WorldDomination.SimpleHosting 7 | Pure Krome 8 | World Domination Technologies 9 | Simple : Hosting 10 | Making it simple to customize Hosting for your ASP.NET Core 6.x+ application 11 | 2020 12 | Library 13 | https://github.com/PureKrome/SimpleHosting 14 | LICENSE.txt 15 | README.md 16 | https://github.com/PureKrome/SimpleHosting 17 | .net c# .net-core 18 | .net dotnet c# netcore aspnetcore aspnet-core hosting world-domination unicorn magicalunicorn magical-unicorn 19 | 20 | 22 | true 23 | 24 | 25 | true 26 | true 27 | true 28 | snupkg 29 | icon.jpg 30 | 31 | 32 | 33 | 34 | 35 | 36 | all 37 | runtime; build; native; contentfiles; analyzers; buildtransitive 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | True 46 | 47 | 48 | 49 | True 50 | 51 | 52 | 53 | True 54 | 55 | 56 | 57 | 58 | 59 | --------------------------------------------------------------------------------