├── .gitignore ├── LICENSE ├── README.md ├── _config.yml ├── appveyor.yml ├── docker ├── docker-compose.yml └── mssql │ └── variables.env └── src └── NetCoreWithDocker ├── NetCoreWithDocker.sln └── NetCoreWithDocker ├── Controllers └── TasksController.cs ├── Migrations ├── 20170730135615_InitialCreate.Designer.cs ├── 20170730135615_InitialCreate.cs ├── 20170730140332_InitialSeed.Designer.cs ├── 20170730140332_InitialSeed.cs └── TasksDbContextModelSnapshot.cs ├── NetCoreWithDocker.csproj ├── Program.cs ├── Startup.cs ├── Storage ├── Entities │ └── Task.cs ├── TasksDbContext.cs └── TasksDbContextFactory.cs ├── appsettings.Development.json └── appsettings.json /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Oskar Dudycz 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Twitter Follow](https://img.shields.io/twitter/follow/oskar_at_net?style=social) [![Build status](https://ci.appveyor.com/api/projects/status/hukklo49h3t2s6hg?svg=true)](https://ci.appveyor.com/project/oskardudycz/netcorewithdocker) [![Github Sponsors](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&link=https://github.com/sponsors/oskardudycz/)](https://github.com/sponsors/oskardudycz/) [![blog](https://img.shields.io/badge/blog-event--driven.io-brightgreen)](https://event-driven.io/) 2 | 3 | # .Net Core With Docker 4 | 5 | This example shows how to: 6 | - [x] setup work environment for .NET Core, 7 | - [x] create simple WebApi project with database usage, 8 | - [x] setup database without needed to install anything more than docker, 9 | - [ ] setup Continuous Integration/Delivery pipeline to make sure that your code runns properly, 10 | - [ ] create test environment, 11 | - [ ] create prod environment. 12 | 13 | ## Support 14 | 15 | Feel free to [create an issue](https://github.com/oskardudycz/EventSourcing.NetCore/issues/new) if you have any questions or request for more explanation or samples. I also take **Pull Requests**! 16 | 17 | 💖 If this repository helped you - I'd be more than happy if you **join** the group of **my official supporters** at: 18 | 19 | 👉 [Github Sponsors](https://github.com/sponsors/oskardudycz) 20 | 21 | ## Setup work environment for .NET Core 22 | 1. Install [Docker](https://www.docker.com/get-docker) 23 | 2. Install [Visual Studio 2017](https://www.visualstudio.com/pl/thank-you-downloading-visual-studio/?sku=Community&rel=15), [VisualStudioCode](https://code.visualstudio.com/download), [Rider](https://www.jetbrains.com/rider/) or your other favourite .NET IDE 24 | 25 | ## Create new Web Api project 26 | We will use default Visual Studio template for the WebApi project: 27 | 1. From the Visual Studio menu select: `File => New => Project` 28 | 2. Choose from the `Templates => Visual C# => .Net Core` an `ASP.NET Core Web Application` 29 | 3. Select also .NET Framework Version on the top (I suggest 4.7), select folder where you want to create new project and the choose the name. 30 | 4. In the new window select `Web Api` template. Unselect "Enable Docker Support" and leave Authentication settings as they are (so no authentication). 31 | 5. Open Package Manager Console and run `dotnet restore` command. It will get all needed NuGet packages for your application. 32 | 6. Now, when you have your basic app setup, you can click `F5` to run it. 33 | 7. If everything went properly then you should see browser page with `http://localhost:{port}/api/values` and `["value1","value2"]`. 34 | 35 | You can check the detailed changes in [pull request](https://github.com/oskardudycz/NetCoreWithDockerCI/pull/2/files) 36 | 37 | ## Add MSSQL Database to the Web Api project 38 | Most of our applications needs to have the database. We'll use Entity Framework and MSSQL server in this example. 39 | 1. From the `Package Manger Console` run `Install-Package Microsoft.EntityFrameworkCore.SqlServer` and `Install-Package Microsoft.EntityFrameworkCore.Tools`. This will add Nuget Packages nessesary for the MSSQL server databasse usage. 40 | 2. Create Entity Class (eg. [Task](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/c3b2dc31fb7ae8b834b94cb338b49fd3a8dbe2b5/src/NetCoreWithDocker/NetCoreWithDocker/Storage/Entities/Task.cs)) and DbContext (eg. [TasksDbContext](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/df641d876b094eb64918c3823ede6a14529216e4/src/NetCoreWithDocker/NetCoreWithDocker/Storage/TasksDbContext.cs)). 41 | 3. You should get simmilar changes as in this [commit](https://github.com/oskardudycz/NetCoreWithDockerCI/pull/4/commits/c3b2dc31fb7ae8b834b94cb338b49fd3a8dbe2b5). 42 | 4. Next step is to provide the connection string to the database. For this example we'll use LocalDB, which is distributed and installed automatically with the Visual Studio 2017 (if you're not using the Visual Studio then you can get it from this [link](https://docs.microsoft.com/en-us/sql/database-engine/configure-windows/sql-server-2016-express-localdb)). 43 | 5. We need to provide proper connection string to [appsettings.json](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/df641d876b094eb64918c3823ede6a14529216e4/src/NetCoreWithDocker/NetCoreWithDocker/appsettings.Development.json) and pass it to the Entity Framework configuration in [Startup.cs](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/df641d876b094eb64918c3823ede6a14529216e4/src/NetCoreWithDocker/NetCoreWithDocker/Startup.cs). 44 | 6. Having that configuration we can remove the dummy `ValuesController` and add new [TasksController](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/df641d876b094eb64918c3823ede6a14529216e4/src/NetCoreWithDocker/NetCoreWithDocker/Controllers/TasksController.cs). 45 | This example contains few important things derived from the best practices like: 46 | * `async` usage - it' much better for the performance perspective to use `async/await` in the api. What's crucial is that you cannot leave the async statement using database not awaited, because it may end up with your db connection not disposed properly, 47 | * returning proper Http Status Codes for the Api endpoints: 48 | * `OK` - will return `200`, 49 | * `NotFound`- will return `404` result, 50 | * `Forbid` - will return `403` status, 51 | 52 | Good explanation of http status codes can be found [here](https://http.cat/) 53 | * few examples of the new usefull [C# 6 syntax](https://msdn.microsoft.com/en-us/magazine/dn802602.aspx) 54 | 7. You should also update your `launchSettings.json` to redirect you by default to the `/tasks/` instead of the `/values/` route. 55 | 8. If you run now you're application then you'll get following exception: 56 | 57 | `` 58 | System.Data.SqlClient.SqlException: 'Cannot open database "NetCoreWithDocker" requested by the login. The login failed. 59 | `` 60 | 61 | It basically means that it cannot login, because in fact there is no database that Entity Framework can login to. How to setup it? By using build in Migrations mechanism. 62 | 9. It's needed to setup the migrations (event the initial one), that will create database with object<=>relation mapping defined in the [TasksDbContext](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/df641d876b094eb64918c3823ede6a14529216e4/src/NetCoreWithDocker/NetCoreWithDocker/Storage/TasksDbContext.cs). To do that open `Package Manager Console` and run: `Add-Migration InitialCreate](). This will automatically freeze the current model definiton in [Current Model Snapshot](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/da1ab7345306933aafcce0002ee4ba54cd437d8b/src/NetCoreWithDocker/NetCoreWithDocker/Migrations/TasksDbContextModelSnapshot.cs) and the define [Initial Migration](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/da1ab7345306933aafcce0002ee4ba54cd437d8b/src/NetCoreWithDocker/NetCoreWithDocker/Migrations/20170730135615_InitialCreate.cs). 63 | 10. Now to create/update our database it's needed to run `Update-Database` from `Package Manager Console`. 64 | 11. You should now be able to run the application. If you did all of the steps properly then you should see browser page with `http://localhost:{port}/api/values` and `[]`. That means that our [TasksController](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/df641d876b094eb64918c3823ede6a14529216e4/src/NetCoreWithDocker/NetCoreWithDocker/Controllers/TasksController.cs) returned empty list of tasks - because our database is currently empty. 65 | 12. Entity Framework also provides mechanism to add initial data (it's usefull for the eg. dictionaries or test data setup). Unfortunatelly it's needed to provide some boilerplate code for that. We need to do following steps: 66 | 67 | 12.1. Run `Add-Migration InitialSeed` in the `Package Manager Console` - this will generate empty migration. 68 | 69 | 12.2. Then we need to prepare our [TasksDbContext](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/df641d876b094eb64918c3823ede6a14529216e4/src/NetCoreWithDocker/NetCoreWithDocker/Storage/TasksDbContext.cs) to be also created not only from the depenedency injection. To do that we should create [TasksDbContextFactory](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/da1ab7345306933aafcce0002ee4ba54cd437d8b/src/NetCoreWithDocker/NetCoreWithDocker/Storage/TasksDbContextFactory.cs). This class will be used for the database configuration (eg. connection string) injection. It needs to implement `IDbContextFactory` and unfortunatelly mimic some of the Startup Configuration reading functionality. Full implementation can be seen [here](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/da1ab7345306933aafcce0002ee4ba54cd437d8b/src/NetCoreWithDocker/NetCoreWithDocker/Storage/TasksDbContextFactory.cs). 70 | 71 | 12.3. Now we can use our factory class in our [Initial Seed](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/da1ab7345306933aafcce0002ee4ba54cd437d8b/src/NetCoreWithDocker/NetCoreWithDocker/Migrations/20170730140332_InitialSeed.cs) migration. In `Up` method we're defining our insertion, in `Down` method we're cleaning data (it' being run if something went wrong). 72 | 73 | 12.4. You need also to mark `appsettings.Development.json` as being coppied to ouput directory. To do that we need to add new settings in the [NetCoreWithDocker.csproj](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/da1ab7345306933aafcce0002ee4ba54cd437d8b/src/NetCoreWithDocker/NetCoreWithDocker/NetCoreWithDocker.csproj). 74 | 75 | Now you should be able to run `Update-Database` from `Package Manager Console` to apply seeding, start application by clicking `F5` and see the results in the browser (eg. `[{"id":1,"description":"Do what you have to do"},{"id":2,"description":"Really urgent task"},{"id":3,"description":"This one has really low priority"}]`) 76 | 77 | You can check the detailed changes in [pull request](https://github.com/oskardudycz/NetCoreWithDockerCI/pull/4/files) 78 | 79 | ## Use MSSQL Database from Docker 80 | 81 | 1. Having docker being installed, now we can setup the docker container with MSSQL database (run on Linux). We'll use [docker-compose](https://docs.docker.com/compose/) tool, which simplyfies docker management, creation (especially for the multiple containers usage). 82 | 2. Let's add new folder `docker` in the root of our project. We will place there all of the docker configuration. Create also subfolder called `mssql`. 83 | 3. In the `docker` folder let's create [docker-compose.yml](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/8758dde3b2f02fb017a09c02612062c024167a4c/docker/docker-compose.yml) - this will be our main configuration file. Docker configs are written in [yaml syntax](https://docs.docker.com/compose/compose-file/). 84 | 4. Our configuration: 85 | ```yaml 86 | version: "3" 87 | services: 88 | mssql: 89 | image: "microsoft/mssql-server-linux" 90 | env_file: 91 | - mssql/variables.env 92 | ports: 93 | - "1433:1433" 94 | ``` 95 | It contains following sections: 96 | * `services` - list of services (docker containers) that will be run, 97 | * `mssql` - name of a service. It is provided by us, it could be named even `xyz`, 98 | * `env_file` - reference to the files with environment needed for our service setup, 99 | * `ports` - mapping of our port. This configuration mean that `1433` port from docker container will be mapped to our localhost `1433` port. Without that configuration port will be by default not accessible. It's also usefull if our local port is in use and we'd like to have different port assigned. 100 | 5. Now let's create [variables.env](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/8758dde3b2f02fb017a09c02612062c024167a4c/docker/mssql/variables.env) file in the `mssql` folder and place there: 101 | ``` 102 | ACCEPT_EULA=Y 103 | SA_PASSWORD=!QAZxsw2#EDC 104 | ``` 105 | * `ACCEPT_EULA` - is needed for accepting MSSQL Server licence terms, 106 | * `SA_PASSWORD` - `sa` database user password 107 | 6. Having this setup ready we can open `CMD` from `docker` directory and run `docker-compose up`. This will download [MSSQL server image](https://hub.docker.com/r/microsoft/mssql-server-linux/) from [Docker Hub](https://hub.docker.com). It will also automatically start the server. 108 | 7. If everything went fine, then you should see `SQL Server is now ready for client connections.` in the `CMD` window. 109 | 8. Now we need to only update our connection strings in [appsettings.json](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/8758dde3b2f02fb017a09c02612062c024167a4c/src/NetCoreWithDocker/NetCoreWithDocker/appsettings.json) and [appsettings.Development.json](https://github.com/oskardudycz/NetCoreWithDockerCI/blob/8758dde3b2f02fb017a09c02612062c024167a4c/src/NetCoreWithDocker/NetCoreWithDocker/appsettings.Development.json) run `Update-Database` from `Package Manager Console` and we can run our application by clicking `F5`! 110 | 9. Piece and cake! 111 | 10. Summary of the [docker-compose](https://docs.docker.com/compose/) CLI can be found [here](https://docs.docker.com/compose/reference/overview/). The most important commands are: 112 | * `docker-compose up` - as described above, gets images and starts containers, 113 | * `docker-compose kill` - kills running dockers, 114 | * `docker-compose pull` - pulls latest docker images, 115 | * `docker system prune` - clean up all containers that were get through `pull` and `up` commands. Useful for cleaning the disk space and making sure that you have the assumed version of docker (`docker system prune` + `docker-compose up`), or just resetting state of the docker container, 116 | * `docker ps` - lists all running docker containers. 117 | 118 | You can check the detailed changes in [pull request](https://github.com/oskardudycz/NetCoreWithDockerCI/pull/6/files) 119 | 120 | 121 | ## Nuget packages to help you get started. 122 | I gathered and generalized all of practices used in this tutorial/samples in Nuget Packages of maintained by me [GoldenEye Framework](https://github.com/oskardudycz/GoldenEye). it provides set of base and bootstrap classes that helps you to reduce boilerplate code and help you focus on writing business code. 123 | See more in: 124 | * [GoldenEye Backend Core package](https://github.com/oskardudycz/GoldenEye/tree/master/src/Core/Backend.Core) - You can find all classes like repositories, etc. and many more. To use it run: 125 | 126 | `dotnet add package GoldenEye.Backend.Core` 127 | * [GoldenEye EntityFramework package](https://github.com/oskardudycz/GoldenEye/tree/master/src/Core/Backend.Core) - You can find here specific implementation of EntityFramework related Repositories, helpers etc. To use it run: 128 | 129 | `dotnet add package GoldenEye.Backend.Core.EntityFramework` 130 | * [GoldenEye WebApi package](https://github.com/oskardudycz/GoldenEye/tree/master/src/Core/Backend.Core.WebApi) - You can find all classes like Base controlers and many more. To use it run: 131 | 132 | `dotnet add package GoldenEye.Backend.Core.WebApi` 133 | 134 | The simplest way to start is **installing the [project template](https://github.com/oskardudycz/GoldenEye/tree/master/src/Templates/SimpleDDD/content) by running** 135 | 136 | `dotnet -i GoldenEye.WebApi.Template.SimpleDDD` 137 | 138 | **and then creating new project based on it:** 139 | 140 | `dotnet new SimpleDDD -n NameOfYourProject` 141 | 142 | 143 | ## Other resources 144 | 145 | # Services 146 | * [MSDN - Background tasks with hosted services in ASP.NET Core](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1) 147 | 148 | # Transactions 149 | * [MSDN - Implementing an Implicit Transaction using Transaction Scope](https://docs.microsoft.com/pl-pl/dotnet/framework/data/transactions/implementing-an-implicit-transaction-using-transaction-scope) 150 | * [Using Transactions](https://docs.microsoft.com/pl-pl/ef/core/saving/transactions) 151 | 152 | # Internals 153 | * [Adam Sitnik - Span](http://adamsitnik.com/Span/) 154 | * [Szymon Kulec - Task, Async Await, ValueTask, IValueTaskSource and how to keep your sanity in modern .NET world](https://blog.scooletz.com/2018/05/14/task-async-await-valuetask-ivaluetasksource-and-how-to-keep-your-sanity-in-modern-net-world/) 155 | 156 | I found an issue or I have a change request 157 | -------------------------------- 158 | Feel free to create an issue on GitHub. Contributions, pull requests are more than welcome! 159 | 160 | **NetCoreWithDocker** is Copyright © 2017-2020 [Oskar Dudycz](http://oskar-dudycz.pl) and other contributors under the [MIT license](LICENSE). 161 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 3.0.{build} 2 | pull_requests: 3 | do_not_increment_build_number: true 4 | skip_branch_with_pr: true 5 | image: Visual Studio 2017 6 | configuration: Release 7 | nuget: 8 | disable_publish_on_pr: true 9 | before_build: 10 | - cmd: dotnet restore src/NetCoreWithDocker/NetCoreWithDocker.sln 11 | build: 12 | project: src/NetCoreWithDocker/NetCoreWithDocker.sln 13 | verbosity: minimal 14 | services: 15 | - mssql2016 16 | -------------------------------------------------------------------------------- /docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | mssql: 4 | image: "microsoft/mssql-server-linux" 5 | env_file: 6 | - mssql/variables.env 7 | ports: 8 | - "1433:1433" -------------------------------------------------------------------------------- /docker/mssql/variables.env: -------------------------------------------------------------------------------- 1 | ACCEPT_EULA=Y 2 | SA_PASSWORD=!QAZxsw2#EDC 3 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2003 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCoreWithDocker", "NetCoreWithDocker\NetCoreWithDocker.csproj", "{22D4B4A7-CB70-431C-9F95-CA90C8FB4FFF}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{98FF9B8E-2E5B-4122-BD89-DD3737728BF7}" 9 | ProjectSection(SolutionItems) = preProject 10 | ..\..\LICENSE = ..\..\LICENSE 11 | ..\..\README.md = ..\..\README.md 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {22D4B4A7-CB70-431C-9F95-CA90C8FB4FFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {22D4B4A7-CB70-431C-9F95-CA90C8FB4FFF}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {22D4B4A7-CB70-431C-9F95-CA90C8FB4FFF}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {22D4B4A7-CB70-431C-9F95-CA90C8FB4FFF}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {9BE7C76D-08C0-4E67-A229-F777B7E43923} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Controllers/TasksController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Microsoft.EntityFrameworkCore; 5 | using NetCoreWithDocker.Storage; 6 | using NetCoreWithDocker.Storage.Entities; 7 | using Threading = System.Threading.Tasks; 8 | 9 | namespace NetCoreWithDocker.Controllers 10 | { 11 | [Route("api/[controller]")] 12 | public class TasksController : Controller 13 | { 14 | private readonly TasksDbContext dbContext; 15 | 16 | private DbSet Tasks => dbContext?.Tasks; 17 | 18 | public TasksController(TasksDbContext dbContext) 19 | { 20 | this.dbContext = dbContext ?? throw new ArgumentException(nameof(dbContext)); 21 | } 22 | 23 | // GET api/tasks 24 | [HttpGet] 25 | public async Threading.Task Get() 26 | { 27 | return Ok(await Tasks.ToListAsync()); 28 | } 29 | 30 | // GET api/tasks/5 31 | [HttpGet("{id}")] 32 | public async Threading.Task Get(int id) 33 | { 34 | var result = await Tasks.FindAsync(id); 35 | 36 | if (result == null) 37 | return NotFound(id); 38 | 39 | return Ok(result); 40 | } 41 | 42 | // POST api/tasks 43 | [HttpPost] 44 | public async Threading.Task Post([FromBody]Task task) 45 | { 46 | if (Tasks.Any(t => t.Id == task.Id)) 47 | return Forbid(); 48 | 49 | Tasks.Add(task); 50 | await dbContext.SaveChangesAsync(); 51 | 52 | return Ok(); 53 | } 54 | 55 | // PUT api/tasks/5 56 | [HttpPut("{id}")] 57 | public async Threading.Task Put(int id, [FromBody]Task task) 58 | { 59 | if (await Tasks.AllAsync(t => t.Id != id)) 60 | return NotFound(task.Id); 61 | 62 | Tasks.Update(task); 63 | await dbContext.SaveChangesAsync(); 64 | 65 | return Ok(); 66 | } 67 | 68 | // DELETE api/tasks/5 69 | [HttpDelete("{id}")] 70 | public async Threading.Task Delete(int id) 71 | { 72 | var result = await Tasks.FindAsync(id); 73 | 74 | if (result == null) 75 | return NotFound(id); 76 | 77 | Tasks.Remove(result); 78 | 79 | return Ok(); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Migrations/20170730135615_InitialCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using NetCoreWithDocker.Storage; 7 | 8 | namespace NetCoreWithDocker.Migrations 9 | { 10 | [DbContext(typeof(TasksDbContext))] 11 | [Migration("20170730135615_InitialCreate")] 12 | partial class InitialCreate 13 | { 14 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 15 | { 16 | modelBuilder 17 | .HasAnnotation("ProductVersion", "1.1.2") 18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 19 | 20 | modelBuilder.Entity("NetCoreWithDocker.Storage.Entities.Task", b => 21 | { 22 | b.Property("Id") 23 | .ValueGeneratedOnAdd(); 24 | 25 | b.Property("Description"); 26 | 27 | b.HasKey("Id"); 28 | 29 | b.ToTable("Tasks"); 30 | }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Migrations/20170730135615_InitialCreate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Microsoft.EntityFrameworkCore.Migrations; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | 6 | namespace NetCoreWithDocker.Migrations 7 | { 8 | public partial class InitialCreate : Migration 9 | { 10 | protected override void Up(MigrationBuilder migrationBuilder) 11 | { 12 | migrationBuilder.CreateTable( 13 | name: "Tasks", 14 | columns: table => new 15 | { 16 | Id = table.Column(nullable: false) 17 | .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn), 18 | Description = table.Column(nullable: true) 19 | }, 20 | constraints: table => 21 | { 22 | table.PrimaryKey("PK_Tasks", x => x.Id); 23 | }); 24 | } 25 | 26 | protected override void Down(MigrationBuilder migrationBuilder) 27 | { 28 | migrationBuilder.DropTable( 29 | name: "Tasks"); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Migrations/20170730140332_InitialSeed.Designer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using NetCoreWithDocker.Storage; 7 | 8 | namespace NetCoreWithDocker.Migrations 9 | { 10 | [DbContext(typeof(TasksDbContext))] 11 | [Migration("20170730140332_InitialSeed")] 12 | partial class InitialSeed 13 | { 14 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 15 | { 16 | modelBuilder 17 | .HasAnnotation("ProductVersion", "1.1.2") 18 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 19 | 20 | modelBuilder.Entity("NetCoreWithDocker.Storage.Entities.Task", b => 21 | { 22 | b.Property("Id") 23 | .ValueGeneratedOnAdd(); 24 | 25 | b.Property("Description"); 26 | 27 | b.HasKey("Id"); 28 | 29 | b.ToTable("Tasks"); 30 | }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Migrations/20170730140332_InitialSeed.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | using NetCoreWithDocker.Storage; 3 | using NetCoreWithDocker.Storage.Entities; 4 | 5 | namespace NetCoreWithDocker.Migrations 6 | { 7 | public partial class InitialSeed : Migration 8 | { 9 | protected override void Up(MigrationBuilder migrationBuilder) 10 | { 11 | using (var db = new TasksDbContextFactory().Create(null)) 12 | { 13 | db.Tasks.AddRange( 14 | new Task { Description = "Do what you have to do" }, 15 | new Task { Description = "Really urgent task" }, 16 | new Task { Description = "This one has really low priority" }); 17 | db.SaveChanges(); 18 | } 19 | } 20 | 21 | protected override void Down(MigrationBuilder migrationBuilder) 22 | { 23 | using (var db = new TasksDbContextFactory().Create(null)) 24 | { 25 | db.Tasks.RemoveRange(db.Tasks); 26 | db.SaveChanges(); 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Migrations/TasksDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Infrastructure; 4 | using Microsoft.EntityFrameworkCore.Metadata; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using NetCoreWithDocker.Storage; 7 | 8 | namespace NetCoreWithDocker.Migrations 9 | { 10 | [DbContext(typeof(TasksDbContext))] 11 | partial class TasksDbContextModelSnapshot : ModelSnapshot 12 | { 13 | protected override void BuildModel(ModelBuilder modelBuilder) 14 | { 15 | modelBuilder 16 | .HasAnnotation("ProductVersion", "1.1.2") 17 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 18 | 19 | modelBuilder.Entity("NetCoreWithDocker.Storage.Entities.Task", b => 20 | { 21 | b.Property("Id") 22 | .ValueGeneratedOnAdd(); 23 | 24 | b.Property("Description"); 25 | 26 | b.HasKey("Id"); 27 | 28 | b.ToTable("Tasks"); 29 | }); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/NetCoreWithDocker.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp2.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | PreserveNewest 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | 9 | namespace NetCoreWithDocker 10 | { 11 | public class Program 12 | { 13 | public static void Main(string[] args) 14 | { 15 | var host = new WebHostBuilder() 16 | .UseKestrel() 17 | .UseContentRoot(Directory.GetCurrentDirectory()) 18 | .UseIISIntegration() 19 | .UseStartup() 20 | .UseApplicationInsights() 21 | .Build(); 22 | 23 | host.Run(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.Extensions.Configuration; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Logging; 10 | using NetCoreWithDocker.Storage; 11 | using Microsoft.EntityFrameworkCore; 12 | 13 | namespace NetCoreWithDocker 14 | { 15 | public class Startup 16 | { 17 | public Startup(IHostingEnvironment env) 18 | { 19 | var builder = new ConfigurationBuilder() 20 | .SetBasePath(env.ContentRootPath) 21 | .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) 22 | .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) 23 | .AddEnvironmentVariables(); 24 | Configuration = builder.Build(); 25 | } 26 | 27 | public IConfigurationRoot Configuration { get; } 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 | // Add framework services. 33 | services.AddMvc(); 34 | ConfigureDb(services); 35 | } 36 | 37 | public void ConfigureDb(IServiceCollection services) 38 | { 39 | var connectionString = Configuration.GetConnectionString("TasksDatabase"); 40 | services.AddDbContext(options => options.UseSqlServer(connectionString)); 41 | } 42 | 43 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 44 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 45 | { 46 | loggerFactory.AddConsole(Configuration.GetSection("Logging")); 47 | loggerFactory.AddDebug(); 48 | 49 | app.UseMvc(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Storage/Entities/Task.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace NetCoreWithDocker.Storage.Entities 7 | { 8 | public class Task 9 | { 10 | public int Id { get; set; } 11 | 12 | public string Description { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Storage/TasksDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | using NetCoreWithDocker.Storage.Entities; 3 | 4 | namespace NetCoreWithDocker.Storage 5 | { 6 | public class TasksDbContext : DbContext 7 | { 8 | public TasksDbContext(DbContextOptions options) 9 | : base(options) 10 | { } 11 | 12 | public DbSet Tasks { get; set; } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/Storage/TasksDbContextFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore; 3 | using Microsoft.EntityFrameworkCore.Design; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.Extensions.Configuration; 6 | 7 | namespace NetCoreWithDocker.Storage 8 | { 9 | public class TasksDbContextFactory : IDesignTimeDbContextFactory 10 | { 11 | public TasksDbContext Create(DbContextFactoryOptions options) 12 | { 13 | var optionsBuilder = new DbContextOptionsBuilder(); 14 | 15 | if (optionsBuilder.IsConfigured) 16 | return new TasksDbContext(optionsBuilder.Options); 17 | 18 | //Called by parameterless ctor Usually Migrations 19 | var environmentName = Environment.GetEnvironmentVariable("EnvironmentName") ?? "Development"; 20 | 21 | var connectionString = 22 | new ConfigurationBuilder() 23 | .SetBasePath(AppContext.BaseDirectory) 24 | .AddJsonFile($"appsettings.{environmentName}.json", optional: true, reloadOnChange: false) 25 | .AddEnvironmentVariables() 26 | .Build() 27 | .GetConnectionString("TasksDatabase"); 28 | 29 | optionsBuilder.UseSqlServer(connectionString); 30 | 31 | return new TasksDbContext(optionsBuilder.Options); 32 | } 33 | 34 | public TasksDbContext CreateDbContext(string[] args) 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Debug", 6 | "System": "Information", 7 | "Microsoft": "Information" 8 | } 9 | }, 10 | "ConnectionStrings": { 11 | "TasksDatabase": "Server=localhost;Database=NetCoreWithDocker;MultipleActiveResultSets=true;User Id=sa;Password=!QAZxsw2#EDC" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/NetCoreWithDocker/NetCoreWithDocker/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "IncludeScopes": false, 4 | "LogLevel": { 5 | "Default": "Warning" 6 | } 7 | }, 8 | "ConnectionStrings": { 9 | "TasksDatabase": "Server=localhost;Database=NetCoreWithDocker;MultipleActiveResultSets=true;User Id=sa;Password=!QAZxsw2#EDC" 10 | } 11 | } 12 | --------------------------------------------------------------------------------