├── .gitignore ├── .markdownlint.json ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── README.md ├── assignments ├── assignment_1.md ├── assignment_2.md ├── assignment_3.md ├── assignment_4.md ├── assignment_5.md ├── bikedata.txt └── project.md ├── game-server-example-code ├── .gitignore ├── NotImplExceptionFilterAttribute.cs ├── Players │ ├── Item.cs │ ├── ItemType.cs │ ├── NewPlayer.cs │ ├── Player.cs │ └── PlayersController.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Repositories │ ├── FileRepository.cs │ ├── IRepository.cs │ └── MongoDbRepository.cs ├── Startup.cs ├── Validation │ └── ValidateModelAttribute.cs ├── appsettings.Development.json ├── appsettings.json └── upgrade.csproj ├── presentations ├── csharp_1.md ├── csharp_2.md ├── intro.md ├── mongodb_1.md ├── mongodb_2.md ├── nosql.md ├── rest.md ├── web_api_1.md ├── web_api_2.md └── web_api_3.md ├── reading ├── reading_week_1.md ├── reading_week_2.md ├── reading_week_3.md └── reading_week_4.md └── resources ├── aspnet-middleware-conf.png ├── aspnet-middleware.png ├── aspnet-simple-pipeline.png ├── claims.png ├── filter-pipeline.png ├── fps-server-architecture.png ├── http-messaging.png ├── nosql-app-integration.png ├── nosql-cap.png ├── nosql-data-distribution.png ├── nosql-data-models.png ├── nosql-hardware.png ├── nosql-history.png ├── nosql-replication.png ├── nosql-sharding.png ├── simple-server-architecture.png ├── social-game-architecture.png └── web-api-beyond-controller.png /.gitignore: -------------------------------------------------------------------------------- 1 | reveal.js/ 2 | citybike/ 3 | -------------------------------------------------------------------------------- /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "no-hard-tabs": false, 4 | "line-length": false, 5 | "no-trailing-punctuation": false 6 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/game-server-example-code/bin/Debug/netcoreapp3.1/game-server-example-code.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/game-server-example-code", 16 | "stopAtEntry": false, 17 | // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser 18 | "serverReadyAction": { 19 | "action": "openExternally", 20 | "pattern": "\\bNow listening on:\\s+(https?://\\S+)" 21 | }, 22 | "env": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | }, 25 | "sourceFileMap": { 26 | "/Views": "${workspaceFolder}/Views" 27 | } 28 | }, 29 | { 30 | "name": ".NET Core Attach", 31 | "type": "coreclr", 32 | "request": "attach", 33 | "processId": "${command:pickProcess}" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/game-server-example-code/game-server-example-code.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/game-server-example-code/game-server-example-code.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/game-server-example-code/game-server-example-code.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Ville Sillanpää 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 | # Game Server programming with ASP.NET Core and MongoDB 2 | 3 | This is a course about game server programming made for Metropolia UAS Game Development curriculum. The aim of the course is to give an overview of creating Web API's suitable for creating online features typical to games. This course is not about creating realtime multiplayer games. 4 | 5 | The technologies chosen for this course are ASP.NET Core and MongoDB. The main reasons for choosing these particular technologies are that they are open source and capable of handling demands of global scale online games. 6 | 7 | The online lectures will be held in Zoom. You will receive the link in the workspace at https://oma.metropolia.fi. 8 | 9 | ## Requirements 10 | 11 | - Basic object oriented programming skills (C# is not a prerequisite) 12 | 13 | ## The weekly schedule 14 | 15 | 1. Intro to game server programming; C# for server development (part 1) 16 | 2. C# for server development (part 2) 17 | 3. ASP.NET Core Web API (part 1); REST-architecture 18 | 4. ASP.NET Core Web API (part 2); MongoDB (part 1) 19 | 5. Intro to NoSQL databases; MongoDB (part 2) 20 | 6. Project work 21 | 7. Student presentations 22 | 23 | ## Way of working 24 | 25 | Read the articles from the reading lists before the lectures where the concepts are presented. 26 | 27 | Try to complete the assignments on the same week they are presented. IMPORTANT: **_Assignments need to be completed in order because they build on top of each other!_** By not completing the assignments in time, you make it harder for yourself to make progress in the course. 28 | 29 | Assignment solutions should be put inside your own GitHub (or similar) repository where I can have access to them. 30 | 31 | The project assignment will be presented on week 2 and it's recommended to start working on it as soon as possible after that. 32 | 33 | ## Tools used in the course 34 | 35 | - VSCode: https://code.visualstudio.com/download 36 | - .Net core SDK: https://www.microsoft.com/net/download 37 | - Omnisharp plugin for VSCode called "C#": https://github.com/OmniSharp/omnisharp-vscode (you can download this inside VSCode from the extensions tab! Search for "C#".) 38 | - MongoDb: https://www.mongodb.com/download-center/community 39 | - PostMan: https://www.getpostman.com/apps 40 | 41 | If you are running Windows and your VSCode uses **powershell** in the terminal (I think it's the default), I recommend that you change it to **CMD**. 42 | 43 | ### Tips for using PostMan 44 | 45 | How to make a simple request: 46 | 47 | - Add API url to the address bar (for example: http://localhost:5000/players) 48 | - Select a suitable Http verb (for example: GET) 49 | - When you want to add a request body: select the "body" tab -> choose "raw" -> change the format from "Text" to "JSON" -> add JSON to the body (for example: `{ "name" : "test" }`) 50 | 51 | ## Grading 52 | 53 | The course grading scheme is straightforward: 54 | 55 | There is a total of 100 points to be gained from the course. 56 | 57 | - 50 points come from the assignments (points are gained by completing the assignments) 58 | - The other 50 comes from the project (points reflect the quality of the project) 59 | 60 | Points are rounded to the closest 10. After rounding the points are converted to the final grades which are numbers from 0 to 5. 61 | 62 | - 50 or less equals 0 (course failed) 63 | - 60 points equals 1 64 | - 70 points equals 2 65 | - 80 points equals 3 66 | - 90 points equals 4 67 | - 100 points equals 5 68 | 69 | You can also affect your grade positively by participating actively in the lectures. 70 | 71 | ## Deadlines 72 | 73 | The deadlines for the assignments and project are the following: 74 | 75 | - Assignment 1 02.09 2020 76 | - Assignment 2 09.09 2020 77 | - Assignment 3 16.09 2020 78 | - Assignment 4 23.09 2020 79 | - Assignment 5 30.09 2020 80 | - Project 07.10 2020 81 | 82 | ## Completing the course without participating the lectures 83 | 84 | It's highly recommended to participate in the lectures because the concepts taught in the course have been generally hard for students to grasp - usually being able to ask questions and get personal support ensures success in the course. In addition to support, being present in the lectures gives you more chances to demonstrate your skills and affect your grade positively. 85 | 86 | But if you nevertheless can not participate in the lectures, the course can be completed by doing the assignments and the project within the deadlines. 87 | 88 | Remember to read the reading materials and slides through carefully, the information found in those should be enough to complete the exercises. 89 | -------------------------------------------------------------------------------- /assignments/assignment_1.md: -------------------------------------------------------------------------------- 1 | # Assignment 1 2 | 3 | The purpose of the following exercise is to get you familiar with .NET concepts to get you ready to do some serious server programming. 4 | 5 | The exercise uses an API for the HSL City Bikes. If you don't care for City Bikes you can feel free to do a variation of this exercise with any other public API. Here are some game related API's (some of which might require registeration): 6 | 7 | https://www.programmableweb.com/news/top-10-games-apis-eve-online-riot-games-battle.net/analysis/2015/11/25 8 | 9 | --- 10 | 11 | ## 1. Create new application 12 | 13 | Create a console application in a empty folder with `dotnet new console` -command. This application will print out the number of available city bikes in a requested station. 14 | 15 | Start by modifying the Program.cs with the following lines of code: 16 | 17 | ```C# 18 | static async Task Main(string[] args) 19 | { 20 | Console.WriteLine(args[0]); 21 | } 22 | ``` 23 | 24 | It will make the application to print the commandline argument passed. 25 | 26 | Run the app with the following command `dotnet run {station_name}` inside the project folder (the `{station_name}` can be any random string at this point). 27 | 28 | --- 29 | 30 | ## 2. Create an interface 31 | 32 | Create the following interface: 33 | 34 | ```C# 35 | public interface ICityBikeDataFetcher 36 | { 37 | Task GetBikeCountInStation(string stationName); 38 | } 39 | ``` 40 | 41 | --- 42 | 43 | ## 3. Add a dependency 44 | 45 | Next we will add a dependency to a JSON-library which we will use to parse the JSON-payload returned from the API into a C# object graph. 46 | 47 | Inside the project folder: 48 | 49 | - Use `dotnet add package NewtonSoft.Json` 50 | - Use `dotnet restore` command to resolve the dependency. 51 | 52 | --- 53 | 54 | ## 4. Create a class implementing the interface 55 | 56 | Create a class called RealTimeCityBikeDataFetcher which implements the ICityBikeDataFetcher and queries the API `http://api.digitransit.fi/routing/v1/routers/hsl/bike_rental` (You can copy paste the API Url into your browser and see what it returns) 57 | 58 | ### Hints for implementation: 59 | 60 | - Create an instance from the class `System.Net.Http.HttpClient`, and use it to do a request with a method called `GetStringAsync` 61 | - There is an example how to use the `HttpClient` here: https://docs.microsoft.com/en-us/dotnet/api/system.net.http.httpclient?view=netcore-3.1 62 | - Remember to use `async` and `await` keywords in your implementation of a asynchronous method. You can make your `Main` method asynchronous as well! (make it return a **Task**). 63 | - Next you need to define your own `BikeRentalStationList` class that correspond the Json the API returns. That is, it should contain properties with same names and types as what is in the Json. 64 | - Deserialize the string to a C# object using `JsonConvert.DeserializeObject` 65 | - Find the information you are looking for from the `BikeRentalStationList` (bike count in a certain station) and return it as the result from the method 66 | 67 | --- 68 | 69 | ## 5. Throw an exception 70 | 71 | Throw ArgumentException (provided by .NET framework) if the user calls the "GetBikeCountInStation" with a string which contains numbers. Catch it in the calling code and print "Invalid argument: " and the message property of the exception. 72 | 73 | --- 74 | 75 | ## 6. Create and throw your own exception 76 | 77 | Create your own Exception called NotFoundException. Throw it if the station can not be found. Catch it in the calling code and print "Not found: " and the message property of the exception. 78 | 79 | --- 80 | 81 | ## 7. Create an alternative implementation 82 | 83 | Create a class called OfflineCityBikeDataFetcher which also implements the ICityBikeDataFetcher. Get a file called `bikedata.txt` from the repository of this course. Find a way to read the requested data from the file. 84 | 85 | --- 86 | 87 | ## 8. Implement commandline arguments 88 | 89 | Make the console application to accept an additional string argument, `offline` or `realtime`, and decide the implementation of ICityBikeDataFetcher based on that. 90 | -------------------------------------------------------------------------------- /assignments/assignment_2.md: -------------------------------------------------------------------------------- 1 | # Assignment 2 2 | 3 | The purpose of the following exercise is to get you familiar with .NET concepts to get you ready to do some serious server programming. 4 | 5 | ## Preparation 6 | 7 | ### Create a new project 8 | 9 | Create a new console application that can be used to run the following exercises. You might remember from the previous assignment that you can create a new console project with `dotnet new console` command. 10 | 11 | ### Create two new classes and an interface 12 | 13 | Create the following classes and interface in separate files inside the project folder. 14 | 15 | ```C# 16 | public interface IPlayer 17 | { 18 | int Score { get; set; } 19 | } 20 | 21 | public class Player : IPlayer 22 | { 23 | public Guid Id { get; set; } 24 | public int Score { get; set; } 25 | public List Items { get; set; } 26 | } 27 | 28 | public class Item 29 | { 30 | public Guid Id { get; set; } 31 | public int Level { get; set; } 32 | } 33 | ``` 34 | 35 | ## 1. Guid 36 | 37 | Implement a function that instantiates 1 000 000 `Player` objects with random Guid as the Id. 38 | 39 | Write code that checks that there are no duplicate Ids among the `Player` objects. 40 | 41 | ## 2. Extension method 42 | 43 | Implement an extension method `GetHighestLevelItem` for `Player` which returns the `Item` with highest level from the item list. 44 | 45 | Write code that creates a player with various items and use the `GetHighestLevelItem` function to find the highest level item and print the result. 46 | 47 | ## 3. LINQ 48 | 49 | Create two variations of a function that takes a `Player` as a parameter and returns all `Item` objects in the list as an array. 50 | 51 | - `GetItems`: Transform the list to an array using normal C# loop to do the work and return it 52 | - `GetItemsWithLinq`: Transform the list to an array by using LINQ extension methods and return it. You can find the documentation for Linq extension methods that are usable with enumerables (like lists) here: https://docs.microsoft.com/en-us/dotnet/api/system.linq?view=netcore-3.1 53 | 54 | Write some runnable code that proves that these functions work. 55 | 56 | ## 4. LINQ 2 57 | 58 | Create two variations of a function that takes a `Player` as a parameter and returns the first `Item` object in the `Item`-list owned by the player. 59 | 60 | - `FirstItem`: Get the first item from a list and return it. If the list is empty, return null. 61 | - `FirstItemWithLinq`: Find the LINQ extension method that does the same thing. You can find the documentation for Linq extension methods that are usable with enumerables (like lists) here: https://docs.microsoft.com/en-us/dotnet/api/system.linq?view=netcore-3.1 62 | 63 | Write some runnable code that uses the functions. 64 | 65 | ## 5. Delegates 66 | 67 | Implement a function with signature `void ProcessEachItem(Player player, Action process);`. This function should call the delegate on each of the `Item` objects owned by the `Player` object. 68 | 69 | Write code that uses the `ProcessEachItem` function to print the `Id` and `Level` of each item to the console. You should write a function with signature `void PrintItem(Item item);` and pass the function as a parameter to `ProcessEachItem`. 70 | 71 | ## 6. Lambda 72 | 73 | Call the `ProcessEachItem` function (implemented in the previous exercise) with a lambda function that does the same thing as the function in the previous excercise (so print the `Id` and `Level` to the console). 74 | 75 | ## 7. Generics 76 | 77 | Create a generic class called `Game`: 78 | 79 | ```C# 80 | public class Game where T : IPlayer 81 | { 82 | private List _players; 83 | 84 | public Game(List players) { 85 | _players = players; 86 | } 87 | 88 | public T[] GetTop10Players() { 89 | // ... write code that returns 10 players with highest scores 90 | } 91 | } 92 | ``` 93 | 94 | Implement the `GetTop10Players` method. 95 | 96 | Write an another class that implements the IPlayer interface called `PlayerForAnotherGame`. 97 | 98 | Write code that demonstrates that you can instantiate the generic `Game` class and call `GetTop10Players` with both `Player` and `PlayerForAnotherGame`. 99 | -------------------------------------------------------------------------------- /assignments/assignment_3.md: -------------------------------------------------------------------------------- 1 | # Assignment 3 2 | 3 | The purpose of the following exercises is to get you familiar with writing API-routes using a layered architecture. Layered architecture means that we divide the responsibilities related to handling the requests between different classes. In our application architecture we have two layers: a controller and a repository. 4 | 5 | After the exercises we have implemented CRUD (Create, Read, Update, Delete) operations for the Players API. 6 | 7 | --- 8 | 9 | ## Preparation 10 | 11 | ### Create new Web API project 12 | 13 | - Create a folder called `GameWebApi` 14 | - Move to the folder 15 | - Run `dotnet new webapi` 16 | - Go to the newly created `Startup.cs` class. Remove the line that says `app.UseHttpsRedirection();`. This is important because the requests to the web api will fail if you don't do this. 17 | 18 | ## 1. Create Model classes 19 | 20 | Create the following classes in separate files inside the project folder: 21 | 22 | `Player` class is used to define objects that are persisted and served to the client. 23 | 24 | ```C# 25 | public class Player 26 | { 27 | public Guid Id { get; set; } 28 | public string Name { get; set; } 29 | public int Score { get; set; } 30 | public int Level { get; set; } 31 | public bool IsBanned { get; set; } 32 | public DateTime CreationTime { get; set; } 33 | } 34 | ``` 35 | 36 | `NewPlayer` class is used to define object that contains the properties than are defined by the client when creating new player. `Id` and `CreationDate` should be set by the server when the player is created. 37 | 38 | ```C# 39 | public class NewPlayer 40 | { 41 | public string Name { get; set; } 42 | } 43 | ``` 44 | 45 | `ModifiedPlayer` class contains the properties that can be modified on a player. 46 | 47 | ```C# 48 | public class ModifiedPlayer 49 | { 50 | public int Score { get; set; } 51 | } 52 | ``` 53 | 54 | ## 2. Create a FileRepository class and IRepository interface 55 | 56 | The responsibility of the `Repository` is to handle accessing and persisting objects. 57 | 58 | Create the following interface in a new file: 59 | 60 | ```C# 61 | public interface IRepository 62 | { 63 | Task Get(Guid id); 64 | Task GetAll(); 65 | Task Create(Player player); 66 | Task Modify(Guid id, ModifiedPlayer player); 67 | Task Delete(Guid id); 68 | } 69 | ``` 70 | 71 | Create a class called `FileRepository` which implements the interface. The purpose of the class is to persist and manipulate the `Player` objects in a text file. One possible solution is to serialize the players as JSON to the text file. The text file name should be `game-dev.txt`. You can use, for example, `File.ReadAllText` and `File.WriteAllText` methods for the implementation. 72 | 73 | You don't need to care about concurrent access to the file at this point. 74 | 75 | --- 76 | 77 | ## 3. Create a PlayersController class 78 | 79 | The first responsibility of the controller is to define the routes for the API. Define the routes using attribute. 80 | 81 | The second responsibility is to handle the business logic. This can include things such as generating IDs when creating a player, and deciding which properties to change when modifying a player. 82 | 83 | `PlayersController` should get `IRepository` through dependency injection and use it to for data access. 84 | 85 | Create a class called `PlayersController`. Add and implement the following methods: 86 | 87 | ```C# 88 | public Task Get(Guid id); 89 | public Task GetAll(); 90 | public Task Create(NewPlayer player); 91 | public Task Modify(Guid id, ModifiedPlayer player); 92 | public Task Delete(Guid id); 93 | ``` 94 | 95 | --- 96 | 97 | ## 4. Register IRepository to DI-container 98 | 99 | Register `FileRepository` to the DI-container in `Startup.cs` - `ConfigureServices`. 100 | 101 | Registering the `FileRepository` as `IRepository` into the dependency injection container enables changing the implementation later on when we start using `MongoDB` as the database. 102 | 103 | --- 104 | 105 | ## 5. Test 106 | 107 | Use `PostMan` to test that the requests to all routes are processed succesfully. 108 | -------------------------------------------------------------------------------- /assignments/assignment_4.md: -------------------------------------------------------------------------------- 1 | # Assignment 4 2 | 3 | _This assignment builds on top of the `GameWebApi` project created in assignment 3._ 4 | 5 | Following exercises will help you get more experience in using ASP.NET Core Web API with the addition of doing server-side validation for the data that the client is sending. 6 | 7 | In the real world, it is very important to get the validation on server-side right. This is true especially for competetive multiplayer games. The validation is done to make sure that players can't cheat in the game by sending illegal data. 8 | 9 | --- 10 | 11 | ## 1. Implement routes (CREATE/READ/UPDATE/DELETE) and data classes for `Item` 12 | 13 | Implement routes and data classes for `Item`. 14 | 15 | ### Tips for the implementation: 16 | 17 | You need at least the following new classes: 18 | 19 | `Item`, `NewItem`, `ModifiedItem`, `ItemsController`. Also, you need to add more methods to the `IRepository` interface and implement them in the `FileRepository`. Now your `IRepository` interface should look like this: 20 | 21 | ```C# 22 | public interface IRepository 23 | { 24 | Task CreatePlayer(Player player); 25 | Task GetPlayer(Guid playerId); 26 | Task GetAllPlayers(); 27 | Task UpdatePlayer(Player player); 28 | Task DeletePlayer(Guid playerId; 29 | 30 | Task CreateItem(Guid playerId, Item item); 31 | Task GetItem(Guid playerId, Guid itemId); 32 | Task GetAllItems(Guid playerId); 33 | Task UpdateItem(Guid playerId, Item item); 34 | Task DeleteItem(Guid playerId, Item item); 35 | } 36 | ``` 37 | 38 | Items should be owned by the players which means that we want to add a list of items (List) to the player class. 39 | 40 | All item routes should start with `.../players/{playerId}/items`. 41 | 42 | --- 43 | 44 | ## 2. MongoDb 45 | 46 | The purpose of this exercise is to learn to write code that accesses data in MongoDb database. You will create a class `MongoDbRepository` which has the responbility to do everything that is related accessing data in MongoDb. It will replace your existing `FileRepository`. 47 | 48 | `MongoDbRepository` should also implement the `IRepository` interface - just like the `FileRepository` does. 49 | 50 | When it's time to run your application with MongoDb, remember to replace the `FileRepository` registeration with the new `MongoDbRepository` in the DI-Container! (in `Startup.cs`) 51 | 52 | ### Tips for the implementation: 53 | 54 | To get the MongoDb driver installed, run the following command in the project folder: `dotnet add package MongoDb.Driver`. 55 | 56 | You need to create a connection to the MongoDb that should be running on your local development machine. If the MongoDb is running with default port, this should work as a connection string: `mongodb://localhost:27017`. 57 | 58 | Look at the example code in this repository to get hints on how to use MongoDb with C#. 59 | 60 | Your data should follow this format: 61 | 62 | - You can name your database to `game` 63 | - Players should be stored in a collection called `players` 64 | - `Items` should be stored in a list inside the `Player` document 65 | 66 | --- 67 | 68 | ## 3. Error handling 69 | 70 | Create your own middleware for handling errors called `ErrorHandlingMiddleware`. 71 | 72 | Create your own exception class called `NotFoundException`. 73 | 74 | Throw the `NotFoundException` when `Player` is not found (incorrect {playerId} passed in the route) when trying to add new `Item`. 75 | 76 | Catch the `NotFoundException` in the `ErrorHandlingMiddleware`. And then on the catch block: set the HTTP status code to 404 (not found) to the `HttpContext` that is passed to the middleware. 77 | 78 | --- 79 | 80 | ## 4. Model validation using attributes 81 | 82 | `NewItem` and `Item` models should have the following properties: 83 | 84 | - int Level 85 | - ItemType Type (define the `ItemType` enum yourself with values SWORD, POTION and SHIELD) 86 | - DateTime CreationDate 87 | 88 | Define the following validations for the model using attributes: 89 | 90 | - "Level can be only within the range from 1 to 99 91 | - "Type" is one of the types defined in the `ItemType` enum 92 | - "CreationDate" is a date from the past (Create custom validation attribute) 93 | 94 | --- 95 | 96 | ## 5 (EXTRA!). Implement a game rule validation in Controller 97 | 98 | This is an extra exercise. You will get bonus points for completing this. 99 | 100 | Implement a game rule validation for the `[POST]` (the one that creates a new item) route in the `ItemsContoller`: 101 | 102 | The rule should be: an item of type of `Sword` should not be allowed for a `Player` below level 3. 103 | 104 | If the rule is not followed, throw your own custom exception (create the exception class) and catch the exception in an `exception filter`. The `exception filter` should write a response to the client with a _suitable error code_ and a _descriptive error message_. The `exception filter` should be only applied to that specific route. 105 | 106 | --- 107 | -------------------------------------------------------------------------------- /assignments/assignment_5.md: -------------------------------------------------------------------------------- 1 | # Assignment 5 2 | 3 | _This assignment builds on top of the `GameWebApi` project created in assignment 3 and extended in assignment 4 and 5._ 4 | 5 | With these exercises you will learn more about creating queries and aggregation against MongoDb. You will also learn how to utilize more advanced query parameters with the Web API. 6 | 7 | You can select from the following assignments the ones that interest you the most. Do atleast `4 queries` but doing more queries and aggregation exercises will yield you extra points. 8 | 9 | There is some help provided for some of the API urls but some of them you have to think on your own. 10 | 11 | --- 12 | 13 | ## Queries 14 | 15 | ### 1. Ranges 16 | 17 | Get `Players` who have more than x score 18 | 19 | **hints:** 20 | 21 | Specify the x in the query like this: `...api/players?minScore=x` 22 | 23 | ### 2. Selector matching 24 | 25 | Get `Player` with a name 26 | 27 | **hints:** 28 | 29 | Make sure the API can handle routes `...api/players/{name}` _and_ `..api/players/{id}` 30 | 31 | You can use attribute constraints or use a query parameter like this: `...api/players?name={name}` 32 | 33 | ### 3. Set operators 34 | 35 | Add `Tags` for the `Player` model ( `Tags` can be a list of strings) and create a query that returns the `Players` that have a certain tag. 36 | 37 | ### 4. Sub documents queries 38 | 39 | Find `Players` who have `Item` with certain property 40 | 41 | ### 5. Size 42 | 43 | Get `Players` with certain amount of `Items` by using size method 44 | 45 | ### 6. Update 46 | 47 | Update `Player` name without fetching the `Player` 48 | 49 | ### 7. Increment 50 | 51 | Increment `Player` score without fetching the `Player` 52 | 53 | ### 8. Push 54 | 55 | Add `Item` to the item list propery on the `Player` 56 | 57 | ### 9. Pop and increment as an atomic operation 58 | 59 | Remove from `Item` from `Player` and add some score for the `Player`. You can think of this as a `Player` selling an `Item` and getting score as a reward. 60 | 61 | **hints:** 62 | 63 | The route should be `..api/players/{playerId}/items/` with DELETE Http verb. 64 | 65 | ### 10. Sorting 66 | 67 | Get top 10 `Players` by score in descending order 68 | 69 | --- 70 | 71 | ## Aggregation 72 | 73 | ### 11. Aggregation exercise based on the example in the slides 74 | 75 | Find out what is the most common level for a player (example in the slides). 76 | 77 | ### 12. Intermediate aggregation exercise 78 | 79 | Get the item counts for different prices for items. 80 | 81 | ### 13. Difficult aggregation exercise 82 | 83 | Get the average score for players who were created between two dates using aggregation. 84 | -------------------------------------------------------------------------------- /assignments/bikedata.txt: -------------------------------------------------------------------------------- 1 | Sammonpuistikko : 3 2 | Hietaniemenkatu : 5 3 | Eteläinen Hesperiankatu : 14 4 | Kesäkatu : 15 5 | Rajasaarentie : 4 6 | Korjaamo : 4 7 | Olympiastadion : 5 8 | Maistraatintori : 0 9 | Messeniuksenkatu : 0 10 | Esterinportti : 0 11 | Uimastadion : 0 12 | Rautatieläisenkatu : 6 13 | Veturitori : 10 14 | Ratapihantie : 2 15 | Venttiilikuja : 14 16 | Linnanmäki : 6 17 | Brahen puistikko : 2 18 | Fleminginkatu : 0 19 | Gebhardinaukio : 3 20 | Jäähalli : 6 21 | Stenbäckinkatu : 12 22 | Töölöntulli : 8 23 | Meilahden sairaala : 7 24 | Jalavatie : 1 25 | Kuusitie : 0 26 | Mäkelänkatu : 29 27 | Kaivopuisto : 26 28 | Tilkanvierto : 2 29 | Lintulahdenkatu : 0 30 | Laivasillankatu : 1 31 | Näkinsilta : 11 32 | Kapteeninpuistikko : 10 33 | Viiskulma : 12 34 | Arielinkatu : 7 35 | Sepänkatu : 14 36 | Kalasataman metroasema : 14 37 | Hietalahdentori : 17 38 | Teurastamo : 4 39 | Designmuseo : 4 40 | Päijänteentie : 3 41 | Vanha kirkkopuisto : 9 42 | Pernajantie : 5 43 | Erottaja : 16 44 | Seurasaari : 25 45 | Saunalahdentie : 6 46 | Torpanranta : 8 47 | Laajalahden aukio : 7 48 | Munkkiniemen aukio : 2 49 | Huopalahdentie : 18 50 | Professorintie : 15 51 | Teollisuuskatu : 1 52 | Kasarmitori : 0 53 | Ulvilantie : 0 54 | Elimäenkatu : 4 55 | Unioninkatu : 13 56 | Muusantori : 13 57 | Kanavaranta : 22 58 | Paavalinpuisto : 7 59 | Merisotilaantori : 11 60 | Haukilahdenkatu : 14 61 | Senaatintori : 27 62 | Velodrominrinne : 9 63 | Ritarikatu : 2 64 | Sofianlehdonkatu : 11 65 | Liisanpuistikko : 11 66 | Arabian kauppakeskus : 13 67 | Varsapuistikko : 2 68 | Arabiankatu : 13 69 | Porthania : 19 70 | Kaironkatu : 12 71 | Rautatientori / itä : 25 72 | Verkatehtaanpuisto : 5 73 | Kaisaniemenpuisto : 21 74 | Intiankatu : 8 75 | Töölönlahdenkatu : 21 76 | Koskelantie : 18 77 | Rautatientori / länsi : 24 78 | Kiasma : 23 79 | Käpyläntie : 3 80 | Mannerheimintie : 14 81 | Pohjolankatu : 5 82 | Narinkka : 4 83 | Pohjolanaukio : 9 84 | Kampin metroasema : 1 85 | Käpylän asema : 10 86 | Eerikinkatu : 0 87 | Juhana Herttuan tie : 17 88 | Lastenlehto : 1 89 | Toinen linja : 14 90 | Baana : 0 91 | Töölönlahden puisto : 4 92 | Itämerentori : 17 93 | Ruomelantie : 7 94 | Marian Sairaala : 0 95 | Friisinkalliontie : 7 96 | Eläinmuseo : 1 97 | Kuunkatu : 12 98 | Kauppakorkeakoulu : 3 99 | Olarinkatu : 6 100 | Kansallismuseo : 4 101 | Piispansilta : 14 102 | Suomenlahdentie : 16 103 | Apollonkatu : 14 104 | Kalastajantie : 7 105 | Töölönkatu : 7 106 | Sepetlahdentie : 6 107 | Töölöntori : 6 108 | Matinkartanontie : 9 109 | Ooppera : 13 110 | Nokkala : 13 111 | Hakaniemen metroasema : 17 112 | Ympyrätalo : 17 113 | Haapaniemenkatu : 10 114 | Karhupuisto : 2 115 | Sörnäisten metroasema : 5 116 | Brahen kenttä : 4 117 | Diakoniapuisto : 4 118 | Kauppatori : 8 119 | Mastokatu : 6 120 | Melkonkuja : 4 121 | Itälahdenkatu : 5 122 | Heikkilänaukio : 8 123 | Heikkiläntie : 6 124 | Gyldenintie : 9 125 | Lahnalahdentie : 13 126 | Luoteisväylä : 4 127 | Lauttasaaren ostoskeskus : 3 128 | Lauttasaarensilta : 4 129 | Porkkalankatu : 1 130 | Länsisatamankatu : 9 131 | Messitytönkatu : 10 132 | Jätkäsaarenlaituri : 11 133 | Tyynenmerenkatu : 23 134 | Hylkeenpyytäjänkatu : 13 135 | Ehrenströmintie : 7 136 | Ulvilanpuisto : 2 137 | Albertinkatu : 19 138 | Munkkivuoren ostoskeskus : 16 139 | Kalevankatu : 0 140 | Niemenmäenkuja : 12 141 | Vihdintie : 3 142 | Kriikunakuja : 16 143 | Tilkantori : 16 144 | Korppaanmäentie : 6 145 | Tenholantie : 8 146 | Radiokatu : 0 147 | Hertanmäenkatu : 3 148 | -------------------------------------------------------------------------------- /assignments/project.md: -------------------------------------------------------------------------------- 1 | # Project assignment 2 | 3 | ## Guidelines 4 | 5 | Create a completely new **game server** project (or extend a school project if you a suitable one) which demonstrates as well as possible what you have learned about the techologies taught in this course. You can even go deeper into these technologies than we do in this course by exploring the documentations. 6 | 7 | There are two outputs for this project: the implementation code and a presentation focused on the implementation of the project. 8 | 9 | ## Teams 10 | 11 | You can work alone or have a team of 2-4 people. I recommended doing this as a team project. 12 | 13 | ## Evaluation criteria 14 | 15 | - The utilization of the information taught on this course. But the purpose is not to copy-paste the home assignments as they are! You should aim to apply your skill in a new context. 16 | - The amount and quality of the features implemented. And the size of the team of course matters. There are higher expectations for a team of four vs a team of two. 17 | - The quality of the presentation/report 18 | - Utilization of self-learned features used will earn you bonus points. So if you dive deep into the documentations of the libraries and frameworks, and find new features and utilize those, you will be rewarded. 19 | 20 | ## Presentation / Report 21 | 22 | The presentation / report should focus on the most interesting/demanding/unique features you have made for the project. 23 | 24 | There are 3 options for the output format: 25 | 26 | 1. Present the project during the last week's class room sessions. The presentation should be ~15 minutes long. 27 | 2. If you can not participate in the class room sessions, create a video presentation 28 | 3. Or create a report about the implementation in a blog post format 29 | 30 | ## Help with the ideas for the project 31 | 32 | If you don’t have an idea for a project you can choose from the following: 33 | 34 | - A leaderboard service 35 | - Admin interface for managing players (adding/modifying/querying/banning etc.) 36 | - A simple asynchronous game (for example tic-tac-toe) 37 | - A simple analytics service 38 | 39 | The focus should be in the server application! So the client side of the application is not relevant in the context of this course. But nevertheless, it would be important to be able to demonstrate the functionalities of the server (for example by using PostMan). 40 | -------------------------------------------------------------------------------- /game-server-example-code/.gitignore: -------------------------------------------------------------------------------- 1 | ################### 2 | # compiled source # 3 | ################### 4 | *.com 5 | *.class 6 | *.dll 7 | *.exe 8 | *.pdb 9 | *.dll.config 10 | *.cache 11 | *.suo 12 | # Include dlls if they’re in the NuGet packages directory 13 | !/packages/*/lib/*.dll 14 | !/packages/*/lib/*/*.dll 15 | # Include dlls if they're in the CommonReferences directory 16 | !*CommonReferences/*.dll 17 | #################### 18 | # VS game-server-example-code stuff # 19 | #################### 20 | game-server-example-codeLog.XML 21 | _game-server-example-codeReport_Files/ 22 | ############### 23 | # Directories # 24 | ############### 25 | bin/ 26 | obj/ 27 | TestResults/ 28 | ################### 29 | # Web publish log # 30 | ################### 31 | *.Publish.xml 32 | ############# 33 | # Resharper # 34 | ############# 35 | /_ReSharper.* 36 | *.ReSharper.* 37 | ############ 38 | # Packages # 39 | ############ 40 | # it’s better to unpack these files and commit the raw source 41 | # git has its own built in compression methods 42 | *.7z 43 | *.dmg 44 | *.gz 45 | *.iso 46 | *.jar 47 | *.rar 48 | *.tar 49 | *.zip 50 | ###################### 51 | # Logs and databases # 52 | ###################### 53 | *.log 54 | *.sqlite 55 | # OS generated files # 56 | ###################### 57 | .DS_Store? 58 | ehthumbs.db 59 | Icon? 60 | Thumbs.db 61 | [Bb]in 62 | [Oo]bj 63 | [Tt]est[Rr]esults 64 | *.suo 65 | *.user 66 | *.[Cc]ache 67 | *[Rr]esharper* 68 | packages 69 | NuGet.exe 70 | _[Ss]cripts 71 | *.exe 72 | *.dll 73 | *.nupkg 74 | *.ncrunchsolution 75 | *.dot[Cc]over -------------------------------------------------------------------------------- /game-server-example-code/NotImplExceptionFilterAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Microsoft.AspNetCore.Mvc.Filters; 4 | 5 | public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute 6 | { 7 | public override void OnException(ExceptionContext context) 8 | { 9 | if (context.Exception is NotImplementedException) 10 | { 11 | context.Result = new NotFoundResult(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /game-server-example-code/Players/Item.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace game_server.Players 4 | { 5 | public class Item 6 | { 7 | public Guid Id { get; set; } 8 | public int Price { get; set; } 9 | public ItemType ItemType { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /game-server-example-code/Players/ItemType.cs: -------------------------------------------------------------------------------- 1 | namespace game_server.Players 2 | { 3 | public enum ItemType 4 | { 5 | Weapon, 6 | Relic, 7 | HealthKit, 8 | } 9 | } -------------------------------------------------------------------------------- /game-server-example-code/Players/NewPlayer.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace game_server.Players 4 | { 5 | public class NewPlayer 6 | { 7 | [StringLength(5)] 8 | public string Name { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /game-server-example-code/Players/Player.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace game_server.Players 5 | { 6 | public class Player 7 | { 8 | public Player() 9 | { 10 | Items = new List(); 11 | } 12 | 13 | public string Id { get; set; } 14 | public string Name { get; set; } 15 | public int Score { get; set; } 16 | public int Level { get; set; } 17 | public bool IsBanned { get; set; } 18 | public DateTime CreationDate { get; set; } 19 | public List Items { get; set; } 20 | } 21 | } -------------------------------------------------------------------------------- /game-server-example-code/Players/PlayersController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Mvc; 3 | using game_server.Validation; 4 | using Microsoft.Extensions.Logging; 5 | using System.Threading.Tasks; 6 | using game_server.Repositories; 7 | 8 | namespace game_server.Players 9 | { 10 | [ApiController] 11 | [Route("players")] 12 | public class PlayersController : ControllerBase 13 | { 14 | private readonly ILogger _logger; 15 | private readonly IRepository _repository; 16 | 17 | public PlayersController(ILogger logger, IRepository repository) 18 | { 19 | _logger = logger; 20 | _repository = repository; 21 | } 22 | 23 | [HttpGet] 24 | [Route("")] 25 | public async Task GetAll(int minLevel) 26 | { 27 | return new Player[] { new Player() { Level = minLevel }, new Player() { Level = (minLevel + 1) } }; 28 | } 29 | 30 | // Filter usage example 31 | [NotImplExceptionFilter] 32 | [HttpGet] 33 | [Route("{playerId}")] 34 | public Task Get(string playerId) 35 | { 36 | throw new NotImplementedException(); 37 | } 38 | 39 | [HttpGet] 40 | [Route("level-counts")] 41 | public Task GetLevelCounts() 42 | { 43 | return _repository.GetLevelCounts(); 44 | } 45 | 46 | [HttpPost] 47 | [Route("")] 48 | [ValidateModel] 49 | public async Task Create([FromBody] NewPlayer newPlayer) 50 | { 51 | _logger.LogInformation("Creating a player with name " + newPlayer.Name); 52 | var player = new Player() 53 | { 54 | Id = Guid.NewGuid().ToString(), 55 | Name = newPlayer.Name 56 | }; 57 | await _repository.CreatePlayer(player); 58 | return player; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /game-server-example-code/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace game_server_example_code 5 | { 6 | public class Program 7 | { 8 | public static void Main(string[] args) 9 | { 10 | CreateHostBuilder(args).Build().Run(); 11 | } 12 | 13 | public static IHostBuilder CreateHostBuilder(string[] args) => 14 | Host.CreateDefaultBuilder(args) 15 | .ConfigureWebHostDefaults(webBuilder => 16 | { 17 | webBuilder.UseStartup(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /game-server-example-code/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:50314", 8 | "sslPort": 44373 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "game-server-example-code": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "weatherforecast", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /game-server-example-code/Repositories/FileRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using game_server.Players; 4 | using game_server.Repositories; 5 | 6 | public class FileRepository //: IRepository 7 | { 8 | public Task GetLevelCounts() 9 | { 10 | throw new NotImplementedException(); 11 | } 12 | 13 | public Task CreateItem(Guid playerId, Item item) 14 | { 15 | throw new NotImplementedException(); 16 | } 17 | 18 | public Task CreatePlayer(Player player) 19 | { 20 | throw new NotImplementedException(); 21 | } 22 | 23 | public Task DeleteItem(Guid playerId, Item item) 24 | { 25 | throw new NotImplementedException(); 26 | } 27 | 28 | public Task DeletePlayer(Guid playerId) 29 | { 30 | throw new NotImplementedException(); 31 | } 32 | 33 | public Task GetAllItems(Guid playerId) 34 | { 35 | throw new NotImplementedException(); 36 | } 37 | 38 | public Task GetAllPlayers() 39 | { 40 | throw new NotImplementedException(); 41 | } 42 | 43 | public Task GetItem(Guid playerId, Guid itemId) 44 | { 45 | throw new NotImplementedException(); 46 | } 47 | 48 | public Task GetPlayer(Guid playerId) 49 | { 50 | throw new NotImplementedException(); 51 | } 52 | 53 | public Task UpdateItem(Guid playerId, Item item) 54 | { 55 | throw new NotImplementedException(); 56 | } 57 | 58 | public Task UpdatePlayer(Player player) 59 | { 60 | throw new NotImplementedException(); 61 | } 62 | } -------------------------------------------------------------------------------- /game-server-example-code/Repositories/IRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using game_server.Players; 4 | 5 | namespace game_server.Repositories 6 | { 7 | public interface IRepository 8 | { 9 | Task CreatePlayer(Player player); 10 | Task GetPlayer(string playerId); 11 | Task GetAllPlayers(); 12 | Task UpdatePlayer(Player player); 13 | Task DeletePlayer(string playerId); 14 | Task GetLevelCounts(); 15 | 16 | Task CreateItem(Guid playerId, Item item); 17 | Task GetItem(Guid playerId, Guid itemId); 18 | Task GetAllItems(Guid playerId); 19 | Task UpdateItem(Guid playerId, Item item); 20 | Task DeleteItem(Guid playerId, Item item); 21 | } 22 | } -------------------------------------------------------------------------------- /game-server-example-code/Repositories/MongoDbRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using MongoDB.Bson; 4 | using game_server.Players; 5 | using System.Linq; 6 | using System.Collections.Generic; 7 | using MongoDB.Driver; 8 | 9 | namespace game_server.Repositories 10 | { 11 | public class MongoDbRepository : IRepository 12 | { 13 | private readonly IMongoCollection _playerCollection; 14 | private readonly IMongoCollection _bsonDocumentCollection; 15 | 16 | public MongoDbRepository() 17 | { 18 | var mongoClient = new MongoClient("mongodb://localhost:27017"); 19 | var database = mongoClient.GetDatabase("game"); 20 | _playerCollection = database.GetCollection("players"); 21 | 22 | _bsonDocumentCollection = database.GetCollection("players"); 23 | } 24 | 25 | public async Task CreatePlayer(Player player) 26 | { 27 | await _playerCollection.InsertOneAsync(player); 28 | return player; 29 | } 30 | 31 | public async Task GetAllPlayers() 32 | { 33 | var players = await _playerCollection.Find(new BsonDocument()).ToListAsync(); 34 | return players.ToArray(); 35 | } 36 | 37 | public Task GetPlayer(string id) 38 | { 39 | var filter = Builders.Filter.Eq(player => player.Id, id); 40 | return _playerCollection.Find(filter).FirstAsync(); 41 | } 42 | 43 | public async Task GetBetweenLevelsAsync(int minLevel, int maxLevel) 44 | { 45 | var filter = Builders.Filter.Gte(p => p.Level, 18) & Builders.Filter.Lte(p => p.Level, 30); 46 | var players = await _playerCollection.Find(filter).ToListAsync(); 47 | return players.ToArray(); 48 | } 49 | 50 | 51 | public Task IncreasePlayerScoreAndRemoveItem(string playerId, Guid itemId, int score) 52 | { 53 | var pull = Builders.Update.PullFilter(p => p.Items, i => i.Id == itemId); 54 | var inc = Builders.Update.Inc(p => p.Score, score); 55 | var update = Builders.Update.Combine(pull, inc); 56 | var filter = Builders.Filter.Eq(p => p.Id, playerId); 57 | 58 | return _playerCollection.FindOneAndUpdateAsync(filter, update); 59 | } 60 | 61 | public async Task UpdatePlayer(Player player) 62 | { 63 | FilterDefinition filter = Builders.Filter.Eq(p => p.Id, player.Id); 64 | await _playerCollection.ReplaceOneAsync(filter, player); 65 | return player; 66 | } 67 | 68 | public async Task GetAllSortedByScoreDescending() 69 | { 70 | var sortDef = Builders.Sort.Descending(p => p.Score); 71 | var players = await _playerCollection.Find(new BsonDocument()).Sort(sortDef).ToListAsync(); 72 | 73 | return players.ToArray(); 74 | } 75 | 76 | public async Task IncrementPlayerScore(string id, int increment) 77 | { 78 | var filter = Builders.Filter.Eq("_id", id); 79 | var incrementScoreUpdate = Builders.Update.Inc(p => p.Score, increment); 80 | var options = new FindOneAndUpdateOptions() 81 | { 82 | ReturnDocument = ReturnDocument.After 83 | }; 84 | Player player = await _playerCollection.FindOneAndUpdateAsync(filter, incrementScoreUpdate, options); 85 | return player; 86 | } 87 | 88 | public async Task DeletePlayer(string playerId) 89 | { 90 | FilterDefinition filter = Builders.Filter.Eq(p => p.Id, playerId); 91 | return await _playerCollection.FindOneAndDeleteAsync(filter); 92 | } 93 | 94 | // Aggregation example 95 | public async Task GetLevelCounts() 96 | { 97 | var levelCounts = 98 | await _playerCollection.Aggregate() 99 | // Have to wrap projected property inside a class. Otherwise it will give an error about being unable to cast bson string to bson document. 100 | .Project(p => new LevelContainer { Level = p.Level }) 101 | // Group LevelContainers so that we have one grouping for each level. Then a count of all documents in a group is performed 102 | .Group(levelContainer => levelContainer.Level, grouping => new LevelCount { Id = grouping.Key, Count = grouping.Select(levelContainer => levelContainer.Level).Count() }) 103 | .SortByDescending(l => l.Count) 104 | .Limit(3) 105 | .ToListAsync(); 106 | 107 | return levelCounts.ToArray(); 108 | } 109 | 110 | public Task CreateItem(Guid playerId, Item item) 111 | { 112 | throw new NotImplementedException(); 113 | } 114 | 115 | public Task GetItem(Guid playerId, Guid itemId) 116 | { 117 | throw new NotImplementedException(); 118 | } 119 | 120 | public Task GetAllItems(Guid playerId) 121 | { 122 | throw new NotImplementedException(); 123 | } 124 | 125 | public Task UpdateItem(Guid playerId, Item item) 126 | { 127 | throw new NotImplementedException(); 128 | } 129 | 130 | public Task DeleteItem(Guid playerId, Item item) 131 | { 132 | throw new NotImplementedException(); 133 | } 134 | } 135 | } 136 | 137 | public class LevelContainer 138 | { 139 | public int Level { get; set; } 140 | } 141 | 142 | public class LevelCount 143 | { 144 | public int Id { get; set; } 145 | public int Count { get; set; } 146 | } 147 | -------------------------------------------------------------------------------- /game-server-example-code/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using game_server.Repositories; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace game_server_example_code 11 | { 12 | public class Startup 13 | { 14 | public Startup(IConfiguration configuration) 15 | { 16 | Configuration = configuration; 17 | } 18 | 19 | public IConfiguration Configuration { get; } 20 | 21 | // This method gets called by the runtime. Use this method to add services to the container. 22 | public void ConfigureServices(IServiceCollection services) 23 | { 24 | services.AddControllers(); 25 | 26 | // Register IRepository implementation to the DI-container 27 | services.AddSingleton(); 28 | } 29 | 30 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 31 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) 32 | { 33 | Console.WriteLine("Is running development: " + env.IsDevelopment()); 34 | 35 | if (env.IsDevelopment()) 36 | { 37 | app.UseDeveloperExceptionPage(); 38 | } 39 | 40 | // Middleware example. You can also write this as classes. 41 | app.Use(async (context, next) => 42 | { 43 | // Do work that doesn't write to the Response. 44 | Console.WriteLine("Before controller"); 45 | 46 | await next.Invoke(); 47 | 48 | // Do other work that doesn't write to the Response. 49 | Console.WriteLine("After controller"); 50 | }); 51 | 52 | 53 | app.UseRouting(); 54 | 55 | app.UseAuthorization(); 56 | 57 | app.UseEndpoints(endpoints => 58 | { 59 | endpoints.MapControllers(); 60 | }); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /game-server-example-code/Validation/ValidateModelAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.Filters; 3 | 4 | namespace game_server.Validation 5 | { 6 | public class ValidateModelAttribute : ActionFilterAttribute 7 | { 8 | public override void OnActionExecuting(ActionExecutingContext actionContext) 9 | { 10 | if (actionContext.ModelState.IsValid == false) 11 | { 12 | actionContext.Result = new BadRequestObjectResult(actionContext.ModelState); 13 | } 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /game-server-example-code/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /game-server-example-code/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /game-server-example-code/upgrade.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /presentations/csharp_1.md: -------------------------------------------------------------------------------- 1 | # C# and .NET essentials for server programming 2 | 3 | --- 4 | 5 | ## Properties 6 | 7 | A flexible and concise **way of manipulating** and **reading private fields** 8 | 9 | Are actually special methods called accessors 10 | 11 | Can be **read-write **/ **read-only** / **write-only** 12 | 13 | ```C# 14 | public class Game { 15 | // An auto-implemented read-write property 16 | public int PlayerCount { get; set; } 17 | 18 | private bool _gameOver; 19 | // A read-write property with explicit backing field 20 | public bool GameOver { 21 | get { return _gameOver; } 22 | set { _gameover = value; } 23 | } 24 | } 25 | ``` 26 | 27 | --- 28 | 29 | ## Interfaces 30 | 31 | Similar to a class – but **provides a specification** rather than an implementation 32 | 33 | Members are **always public** 34 | 35 | Members can be **methods**, **properties**, **events** or **indexers** 36 | 37 | Members will be implemented by the classes and structs that implement the interface 38 | 39 | ```C# 40 | public interface IEnumerator { 41 | bool MoveNext(); 42 | object Current { get; } 43 | } 44 | ``` 45 | 46 | --- 47 | 48 | ### Implementing an interface 49 | 50 | Done by providing an implementation for all members 51 | 52 | ```C# 53 | public class Countdown : IEnumerator { 54 | int count = 11; 55 | public bool MoveNext () { return count-- > 0 ; } 56 | public object Current { get { return count; } } 57 | } 58 | ``` 59 | 60 | A class (or struct) can implement multiple interfaces 61 | 62 | --- 63 | 64 | ### Interfaces: benefits 65 | 66 | Write reusable code 67 | 68 | - For objects created from classes **that implement the same interface** 69 | 70 | --- 71 | 72 | ```C# 73 | public interface IAnimal { 74 | void MakeSound(); 75 | } 76 | public class Cat : IAnimal { 77 | public void MakeSound(){ 78 | // meow 79 | } 80 | } 81 | public class Dog : IAnimal { 82 | public void MakeSound() { 83 | // woof 84 | } 85 | } 86 | ``` 87 | 88 | You can call this with object of **Cat** or **Dog**: 89 | 90 | ```cs 91 | public MakeNoise(IAnimal animal) { 92 | animal.MakeSound(); 93 | } 94 | ``` 95 | 96 | --- 97 | 98 | ### So why reusable code? 99 | 100 | Change application behavior by changing the implementation 101 | 102 | Make testing easier by creating mock implementations 103 | 104 | --- 105 | 106 | ## Exceptions 107 | 108 | A **try** statement specifies a **code block subject to error-handling or cleanup code** 109 | 110 | The **catch** block executes **when exception is thrown** in the try block 111 | 112 | ```C# 113 | try { 114 | DoStuff(); 115 | DoSomethingElse(); 116 | } 117 | catch(Exception e) { 118 | Console.WriteLine("Error happened: " +e) 119 | } 120 | ``` 121 | 122 | --- 123 | 124 | ### Example of a catch clause 125 | 126 | ```C# 127 | static void Main (string[] args) { 128 | try { 129 | byte b = byte.Parse (args[0]); 130 | Console.WriteLine (b); 131 | } 132 | catch (IndexOutOfRangeException ex) { 133 | Console.WriteLine ("Please provide at least one argument"); 134 | } 135 | catch (FormatException ex) { 136 | Console.WriteLine ("That's not a number!"); 137 | } 138 | catch (Exception ex) { 139 | Console.WriteLine ("We caught something else") 140 | } 141 | } 142 | ``` 143 | 144 | --- 145 | 146 | ### The catch clause 147 | 148 | Specifies what type of exception to catch 149 | 150 | Must either be System.Exception or a subclass of System.Exception 151 | 152 | Catching System.Exception catches all possible errors 153 | 154 | --- 155 | 156 | ### Choosing the catch block 157 | 158 | Evaluating which catch clause to execute is done from top to bottom 159 | 160 | Only one catch clause is executed 161 | 162 | More specific exceptions must be placed above the more generic 163 | 164 | Typically you catch more specific exception types 165 | 166 | - Avoids dealing with situations you were not expecting (e.g. OutOfMemoryException) 167 | 168 | --- 169 | 170 | ### The finally block 171 | 172 | Finally block is always run, either: 173 | 174 | - After a catch block finishes 175 | - After control leaves the try block because of a jump statement (return etc.) 176 | - After the try block ends 177 | 178 | Typically used for cleanup: 179 | 180 | ```C# 181 | static void ReadFile() { 182 | StreamReader reader = null; 183 | try { 184 | reader = File.OpenText ("file.txt"); 185 | if (reader.EndOfStream) return; 186 | Console.WriteLine (reader.ReadToEnd()); 187 | } finally { 188 | if (reader != null) reader.Dispose(); 189 | } 190 | } 191 | ``` 192 | 193 | --- 194 | 195 | ### Throwing exceptions 196 | 197 | ```C# 198 | static void Display (string name) { 199 | if (name == null) throw new ArgumentNullException ("name"); 200 | Console.WriteLine (name); 201 | } 202 | ``` 203 | 204 | Exceptions can be rethrown: 205 | 206 | ```C# 207 | try { 208 | Display("John"); 209 | } 210 | catch (ArgumentNullException e) { 211 | Console.WriteLine("Error: " e.Message); 212 | throw e; 213 | } 214 | ``` 215 | 216 | --- 217 | 218 | ### Key properties in an exception 219 | 220 | **Stacktrace** 221 | 222 | - a string representing all the methods that are called from the origin of the exception to the catch block 223 | 224 | **Message** 225 | 226 | - A string with a description of the error 227 | 228 | **InnerException** 229 | 230 | - The inner exception (if any) that caused the outer exception 231 | 232 | --- 233 | 234 | ## Tasks and asynchronous programming 235 | 236 | Servers often need to deal with more than one thing happening at a time (concurrency) 237 | 238 | Multithreading is the way of creating concurrency 239 | 240 | - Tasks are a higher-level abstraction on top of threads 241 | 242 | Many libraries provide asynchronous versions of operations which utilize Tasks 243 | 244 | Thus, it’s important to know the basics of using Tasks 245 | 246 | --- 247 | 248 | ### Asynchronous functions 249 | 250 | **async** and **await** are keywords used for asynchronous programming in C# 251 | 252 | - These let you write asynchronous code with **same structure and simplicity as synchronous code** 253 | 254 | You can apply await-keyword to tasks: 255 | 256 | ```C# 257 | public async Task GetPlayerAndPrintNameAsync(Guid id) { 258 | // GetPlayerAsync returns a Task 259 | IPlayer player = await database.GetPlayerAsync(id); 260 | Console.WriteLine("Got player named: " +player.Name); 261 | } 262 | 263 | // Synchronous version of the same method: 264 | public void GetPlayerAndPrintName(Guid id) { 265 | IPlayer player = database.getPlayer(id); 266 | Console.WriteLine("Got player named: " +player.Name); 267 | } 268 | ``` 269 | 270 | --- 271 | 272 | ### Asynchronous functions 273 | 274 | The **async** modifier can be applied only to methods that return **void** or **Task** or **`Task`** 275 | 276 | - There is virtually no reason to return void ever though 277 | 278 | You can create your own asynchronous IO functions but that is out of the scope of this course 279 | 280 | --- 281 | 282 | ## Json (JavaScript object notation) 283 | 284 | A simple example: 285 | 286 | ```json 287 | { 288 | "_id": "57ade8face4bf5361c4268d6", 289 | "Name": "john", 290 | "Score": 7, 291 | "Items": [ 292 | { 293 | "_id": "57ade921ce4bf5361c4268d7", 294 | "ItemType": 0 295 | } 296 | ] 297 | } 298 | ``` 299 | 300 | --- 301 | 302 | ### Json 303 | 304 | Notation used to **describe objects** 305 | 306 | Many APIs communicate in JSON or XML (or both) 307 | 308 | **Language independent** 309 | 310 | Has the following datatypes: 311 | 312 | - Number 313 | - String 314 | - Object 315 | - Array 316 | - Boolean 317 | - null 318 | 319 | Newtonsoft.Json library is often used for handling JSON data with .NET 320 | -------------------------------------------------------------------------------- /presentations/csharp_2.md: -------------------------------------------------------------------------------- 1 | # C# and .NET essentials for server programming 2 | 3 | ## part 2 4 | 5 | --- 6 | 7 | ## Guid 8 | 9 | Globally unique identifier 10 | 11 | Example: “644e1dd7-2a7f-18fb-b8ed-ed78c3f92c2b” 12 | 13 | 128-bit integer (16 bytes) 14 | 15 | Very low probability of being duplicate 16 | 17 | Creation: Guid id = Guid.NewGuid(); 18 | 19 | Note: 20 | 21 | Probablity: This number is equivalent to generating 1 billion UUIDs per second for about 85 years, and a file containing this many UUIDs, at 16 bytes per UUID, would be about 45 exabytes, many times larger than the largest databases currently in existence, which are on the order of hundreds of petabytes. (Wikipedia) 22 | 23 | --- 24 | 25 | ## Extension methods 26 | 27 | Allow extending a type with new methods without altering the original definition 28 | 29 | An extension method is a static method of a static class, where the this modifier is applied to the first parameter 30 | 31 | --- 32 | 33 | ### Extension method example 34 | 35 | ```C# 36 | public static class StringHelper { 37 | public static bool IsCapitalized (this string s) { 38 | if (string.IsNullOrEmpty(s)) return false; 39 | return char.IsUpper (s[0]); 40 | } 41 | } 42 | ``` 43 | 44 | Can be called like this: 45 | 46 | ```C# 47 | bool isCapitalized = “John”.IsCapitalized(); 48 | ``` 49 | 50 | Note: 51 | 52 | Needed for doing the .NET Core middleware registration in the designed way 53 | 54 | Just syntactic sugar 55 | 56 | --- 57 | 58 | ## Attributes 59 | 60 | ```C# 61 | [Route("/players/", Name = "CreatePlayerRoute")] 62 | [ValidateModel] 63 | [HttpPost] 64 | public async Task CreatePlayer( 65 | HttpRequestMessage requestMessage, 66 | NewPlayer newPlayer) { ...} 67 | ``` 68 | 69 | Attributes are an extensible mechanism for adding custom information to code elements 70 | 71 | Attribute parameters fall into one of two categories: positional or named 72 | 73 | - In the “Route” attribute above, the first argument is a positional parameter; the second is a named parameter 74 | 75 | Note: 76 | 77 | You can create your own attributes by extending System.Attribute 78 | 79 | - All attribute type names should end with a word “Attribute” by convention 80 | 81 | You can specify multiple attributes for a single code element 82 | 83 | Positional parameters correspond to parameters of the attribute type’s public constructors. Named parameters correspond to public fields or public properties on the attribute type. 84 | 85 | --- 86 | 87 | ## LINQ 88 | 89 | Language-integrated query 90 | 91 | Extension methods (and query expressions) for querying collections and other data sources 92 | 93 | Basically replacing foreach loops with the LINQ approach 94 | 95 | Very common to see LINQ being used in C# code 96 | 97 | --- 98 | 99 | ### LINQ example 100 | 101 | Typical examples are .ToArray() and .First() 102 | 103 | Easily converting a list to an array: 104 | 105 | ```C# 106 | var list = new List(); 107 | var array = list.ToArray(); 108 | ``` 109 | 110 | --- 111 | 112 | ## Delegates 113 | 114 | A delegate is an **object that knows how to call a method** 115 | 116 | The following defines a delegate type called Transformer: 117 | 118 | ```C# 119 | delegate int Transformer (int x); 120 | ``` 121 | 122 | A delegate type defines the kind of method that delegate instances can call: 123 | 124 | - Return type 125 | - Parameters 126 | 127 | --- 128 | 129 | ### Delegates 130 | 131 | Transformer is compatible with any method with an int return type and a single int parameter: 132 | 133 | ```C# 134 | static int Square (int x) { return x * x; } 135 | ``` 136 | 137 | Assigning a method to a delegate variable creates a delegate instance 138 | 139 | ```C# 140 | Transformer t = Square; 141 | ``` 142 | 143 | which can be invoked in the same way as a method 144 | 145 | ```C# 146 | int answer = t(3); // answer is 9 147 | ``` 148 | 149 | --- 150 | 151 | ### Delegates 152 | 153 | A delegate instance literally acts as a delegate for the caller 154 | 155 | - The caller invokes the delegate 156 | - The delegate calls the target method 157 | 158 | Decouples the caller from the target method 159 | 160 | ```C# 161 | app.Use(async (context, next) => { 162 | // Do work before next middleware 163 | await next.Invoke(); // We don't need to know what "next" is here. 164 | // Do work after the next middleware returns 165 | }); 166 | ``` 167 | 168 | --- 169 | 170 | ## Action-delegate 171 | 172 | Action is a delegate type defined in the System namespace 173 | 174 | ```C# 175 | public delegate void Print(string value); 176 | 177 | public static void ConsolePrint(string value) { 178 | Console.WriteLine(value); 179 | } 180 | 181 | public static void PrintTest() { 182 | Print printFunction = ConsolePrint; 183 | printFunction("test"); 184 | } 185 | 186 | public static void PrintTestWithAction() { 187 | Action printFunction = ConsolePrint; 188 | printFunction("test"); 189 | } 190 | ``` 191 | 192 | Note: 193 | 194 | - Can have up to 16 parameters 195 | 196 | --- 197 | 198 | ## Func-delegate 199 | 200 | Func is the similar as Action-delegate but it **has a return value** 201 | 202 | `Func` is a delegate that takes a function that: 203 | 204 | - has no parameters 205 | - returns an `int` 206 | 207 | `Func` is a delegate that takes a function that: 208 | 209 | - has one parameter with `int`-type 210 | - returns an `int` 211 | 212 | --- 213 | 214 | ## Lambda expressions 215 | 216 | A lambda expression is an **unnamed method written in place of a delegate instance** 217 | 218 | A lambda expression has the following form: 219 | 220 | - (parameters) => expression-or-statement-block 221 | 222 | An example of a lambda expression: 223 | 224 | ```C# 225 | x => x * x 226 | // ‘function’ that has a parameter x in which x 227 | // is multiplied with x and the result is returned 228 | ``` 229 | 230 | A lambda expression can be a statement block instead of an expression 231 | 232 | ```C# 233 | x => { return x * x; }; 234 | ``` 235 | 236 | Note: 237 | 238 | Parentheses can be omitted if there is exactly one parameter 239 | 240 | --- 241 | 242 | ## Generics 243 | 244 | ```C# 245 | public class Stack { 246 | int position; 247 | T[] data = new T[100]; 248 | public void Push (T obj) { data[position++] = obj; } 249 | public T Pop() { return data[--position]; } 250 | } 251 | 252 | ``` 253 | 254 | `Stack` fills in the type parameter T with the type argument int 255 | 256 | `Stack` is an open type, whereas `Stack` is a closed type 257 | 258 | At runtime, all generic type instances are closed 259 | 260 | Note: 261 | 262 | Explain with an example why generics are useful 263 | 264 | Why not hardcode a separate version for each type 265 | 266 | Why not have just a stack with type “object” 267 | 268 | --- 269 | 270 | ### Generics 271 | 272 | A **mechanism for writing reusable code** across different types 273 | 274 | Generics, when compared to inheritance, can increase type safety and reduce casting and boxing 275 | 276 | Classes, interfaces and methods can be generic 277 | -------------------------------------------------------------------------------- /presentations/intro.md: -------------------------------------------------------------------------------- 1 | # Game server programming 2 | 3 | --- 4 | 5 | ## The focus 6 | 7 | Building **Web APIs** for games 8 | 9 | Get familiar with **NoSQL** databases 10 | 11 | --- 12 | 13 | ### What do we actually do? 14 | 15 | Create Web APIs for games using **ASP.NET Core (C#)** 16 | 17 | Use **MongoDb** as the database for the Web API 18 | 19 | Learn some best practices for server development 20 | 21 | Note: 22 | 23 | - HTTP APIs are more general purpose than real-time implementations 24 | 25 | --- 26 | 27 | ### Why these things? 28 | 29 | Web APIs play an important role in most online games 30 | 31 | NoSQL databases are more suited for global scale products 32 | 33 | A solid backend is a requirement for a succesful online game 34 | 35 | Note: 36 | 37 | - Important to understand NoSQL databases especially in terms of performance, availability and scalability 38 | - What's great about this? Learnings can be applied to a great variety of projects - even outside games 39 | 40 | --- 41 | 42 | ### Why should you care? 43 | 44 | If you are going to do backend programming as your day job, it's fairly obvious 45 | 46 | Otherwise you still need to understand: 47 | 48 | - the limitations 49 | - the possibilities 50 | - the cost 51 | - the requirements 52 | 53 | --- 54 | 55 | ## Game server development 56 | 57 | Note: 58 | 59 | - Go through the Readme.md before moving forward 60 | 61 | --- 62 | 63 | ### What can we do with a server? 64 | 65 | Player to player interaction 66 | 67 | Share collective game state between players 68 | 69 | Persist the game data 70 | 71 | Authorize player actions (prevent cheating) 72 | 73 | Gather and analyze data 74 | 75 | Do simulation that is too expensive for the client 76 | 77 | Note: 78 | 79 | - Interaction: can be anything from real-time multiplayer to chat and facilitating in-game economies 80 | - Persisting data: saving profiles, player items etc. 81 | - Authorizing: a must for competitive games. The assumption is that the client can always be hacked and thus all critical client actions should be verified by the server 82 | - Data analytics is a growing field in gaming 83 | 84 | --- 85 | 86 | ### The cost 87 | 88 | Developing and maintaining the server(s) as long as the game is alive 89 | 90 | Server-side usually multiplies the complexity of the client 91 | 92 | Servers and network traffic create operating costs 93 | 94 | Popular multiplayer games will be targeted by hackers and cheaters 95 | 96 | Online games often require “extra” features 97 | 98 | Note: 99 | 100 | - Maintenance: getting easier with SaaS and PaaS 101 | - Complexity: There is always latency (and the amount varies) 102 | - Complexity: Network debugging is a lot more difficult than debugging a local game 103 | - Complexity: Bandwidth is often more limited than the amount of information that we would like to send (in real-time games) 104 | - Real-time games need to have a lot of users online at all times since they are the content of the game 105 | 106 | --- 107 | 108 | ## So, how does a server solution look like? 109 | 110 | --- 111 | 112 | ### Example: a simple game server architecture 113 | 114 | ![Simple game server architecture](/resources/simple-server-architecture.png) 115 | 116 | --- 117 | 118 | ### Example: Imaginary FPS game server architecture 119 | 120 | ![FPS game server architecture](/resources/fps-server-architecture.png) 121 | 122 | --- 123 | 124 | ## Few important concepts 125 | 126 | --- 127 | 128 | ## Network topologies 129 | 130 | --- 131 | 132 | ### Peer-to-Peer (client-to-client) 133 | 134 | All clients participating in the same game instance are interconnected with each other 135 | 136 | All clients are equal (no hierarchies) 137 | 138 | Limited scaling potential 139 | 140 | - Amount of connections grows exponentially with each client 141 | 142 | Has been popular choice in the early days of multiplayer gaming 143 | 144 | There is no persistence or data collection, cheat prevention is hard, and all simulation is on client side 145 | 146 | Note: 147 | 148 | - Still viable in games like fighting games where there are limited amount of players interacting with each other 149 | 150 | --- 151 | 152 | ### Client-Server 153 | 154 | There is a separate server application 155 | 156 | All communications go through the server 157 | 158 | Server is an extra step on the communication 159 | 160 | - But there are various ways to optimize the traffic 161 | 162 | Server enables all the good things we have discussed before... 163 | 164 | Note: 165 | 166 | - In some cases we might have one client promoted to a server 167 | - Server can filter the data 168 | - Server can aggregate the packets and smooth out the packet flow 169 | 170 | --- 171 | 172 | ## Asynchronous vs synchronous multiplayer games 173 | 174 | --- 175 | 176 | ### Asynchronous 177 | 178 | Messaging is not time critical 179 | 180 | Players can even play with each other without playing it at the same time 181 | 182 | Actions are usually validated against the game state in database 183 | 184 | No need for latency compensation on the client side 185 | 186 | Cheaper to run than... 187 | 188 | Note: 189 | 190 | - Usually implemented with TCP protocol 191 | 192 | --- 193 | 194 | ### Synchronous 195 | 196 | Messaging is time critical 197 | 198 | Players are playing the game at the same time 199 | 200 | Actions are usually validated against real-time simulation on server-side 201 | 202 | Often needs latency compensation techniques on the client side 203 | 204 | More costly to run 205 | 206 | Note: 207 | 208 | - Might need as many as 10 updates per second 209 | - Often implemented with UDP 210 | - UDP is more work intensive: handling dropped packets, handling the connection, flow control etc. 211 | 212 | --- 213 | 214 | ## Cloud Services 215 | 216 | This subject would need its own course... 217 | 218 | --- 219 | 220 | ### Cloud services 221 | 222 | Nowadays **most online games are deployed to public cloud** 223 | 224 | Viable even for smaller game studios 225 | 226 | - No big initial investments - just pay for what you use 227 | 228 | Useful out of the box solutions 229 | 230 | Cloud can scale up and down very quickly 231 | 232 | Ease of deploying to datacenters around the world 233 | 234 | Most commonly used public clouds are Amazon AWS and Microsoft Azure 235 | 236 | --- 237 | 238 | ### Commonly used cloud services 239 | 240 | PaaS solutions for the code run-time environment 241 | 242 | SaaS databases 243 | 244 | Data analytics related services 245 | 246 | Security and identity services 247 | 248 | And many more… 249 | 250 | Note: 251 | 252 | There are four different cloud computing service models: IaaS, PaaS, SaaS and FaaS 253 | 254 | --- 255 | 256 | ## So you decide to build an online game 257 | 258 | --- 259 | 260 | ### Things you need to know 261 | 262 | How to build great Web APIs 263 | 264 | Databases – both traditional SQL and NoSQL databases 265 | 266 | How to build scalable, stable, and fault tolerant systems 267 | 268 | How to build security and cheat resistancy 269 | 270 | How to utilize the publis cloud services 271 | 272 | How to build robust realtime experiences 273 | 274 | Note: 275 | 276 | In this course we'll focus mostly on the first 2 - and bit on #3 and #4. 277 | 278 | --- 279 | 280 | ## But, why to (learn to) make your own solution? 281 | 282 | --- 283 | 284 | ### 3rd party services 285 | 286 | Provide readymade solutions for the common problems: 287 | 288 | - Account management 289 | - Realtime multiplayer 290 | - Commerce 291 | - Analytics 292 | - Social features (chat, leaderboard, friends etc.) 293 | - etc. 294 | 295 | These can reduce the game server development time significantly 296 | 297 | There are many viable providers out there such as GameSparks and PlayFab 298 | 299 | --- 300 | 301 | ### Considerations for 3rd party services 302 | 303 | Limits to your designs 304 | 305 | Dependecy to the service provider 306 | 307 | Trustworthiness of the service provider 308 | 309 | Giving access to your valuable data 310 | 311 | Black box implementations: you might not be able see or touch the code 312 | 313 | Surpises: updates with breaking changes, company going bankcrupt, downtimes, pricing model changes... 314 | 315 | Note: 316 | 317 | - Data can be one of the more valuable assets of a game company 318 | -------------------------------------------------------------------------------- /presentations/mongodb_1.md: -------------------------------------------------------------------------------- 1 | # MongoDB Part 1: basics 2 | 3 | --- 4 | 5 | ## What is MongoDB 6 | 7 | Open source, written in C++, and licenced under GNU-AGPL 8 | 9 | - https://github.com/mongodb/mongo 10 | 11 | High availability 12 | 13 | Automatic scaling 14 | 15 | Document-oriented data model 16 | 17 | Compiles in Max OS X, Windows, and most flavors of Linux 18 | 19 | Note: 20 | 21 | The GNU-AGPL is subject to some controversy. What this licensing means in practice is that the source code is freely available and the contributions from the community are encouraged. The primary limitation of the GNU-AGPL is that any modifications made to the source code must be published publicly for the benefit of the community . 22 | 23 | Who uses MongoDB: 24 | 25 | - EA 26 | - Square Enix 27 | - Grand Cru 28 | 29 | Can be used as primary data store unlike many key-value stores (usually) 30 | Not as fast as key-value stores 31 | 32 | --- 33 | 34 | ## In a nutshell 35 | 36 | JSON-like structure to represent data 37 | 38 | Strong consistency 39 | 40 | Combine the best features of relational databases and distributed key-value stores 41 | 42 | Ad hoc queries – no need to define queries that the system will accept in advance 43 | 44 | Secondary indexes 45 | 46 | Only a few configuration parameters 47 | 48 | Note: 49 | 50 | - Just like with relational databases 51 | - Adhoc queries 52 | - JSON is an acronym for JavaScript Object Notation. JSON structures are comprised of keys and values, and they can nest arbitrarily deep. They’re analogous to the dictionaries and hash maps of other programming languages. 53 | - JSON is simple, intuitive and human friendly 54 | - Which features? 55 | - Key-value stores, because of their simplicity, are extremely fast and relatively easy to scale. 56 | - Relational databases are more difficult to scale, at least horizontally, but admit a rich data model and a powerful query language. 57 | - Key-value stores only support queries with key 58 | - With big data sets indexes are necessary 59 | - Configuration: for example the memory management is offloaded to the OS kernel 60 | 61 | --- 62 | 63 | ## History 64 | 65 | Created by startup called 10gen 66 | 67 | Started as a more ambitious platform project in mid-2007 68 | 69 | Similar to Google AppEngine 70 | 71 | Was meant to handle scaling and managing of software and hardware infrastructure automatically 72 | 73 | Developers didn’t want to give up that much control but wanted the database technology instead 74 | 75 | MongoDB v1.0 was released in November 2009 76 | 77 | Note: 78 | 79 | - software platform-as-a-service, composed of an application server and a database, that would host web applications and scale them as needed. 80 | 81 | --- 82 | 83 | ## Example of MongoDB document 84 | 85 | ```json 86 | { 87 | "_id": "57ade8face4bf5361c4268d6", 88 | "Name": "john", 89 | "Score": 7, 90 | "IsBanned": false, 91 | "CreationDate": ISODate("2016-08-12T15:19:22.492Z"), 92 | "Items": [ 93 | { 94 | "_id": "57ade921ce4bf5361c4268d7", 95 | "Price": 5, 96 | "ItemType": 0 97 | } 98 | ] 99 | } 100 | ``` 101 | 102 | Note: 103 | 104 | - Set of property names and their values 105 | - Documents can contain subdocuments 106 | - What’s important to note here is that a document-oriented data model naturally represents data in an aggregate form, allowing you to work with an object holistically 107 | - Documents don’t need to conform to predefined schema 108 | 109 | --- 110 | 111 | ## Document-oriented data model 112 | 113 | Documents are grouped into collections without any schema 114 | 115 | Naturally represents the data in an aggregate form, allowing to work on it holistically 116 | 117 | Application forces the data structure 118 | 119 | Can speed up development when the schema is changing frequently, especially true with games! 120 | 121 | Note: 122 | 123 | - In practice documents in a collection will be relatively uniform 124 | - No need for alter table type of commands 125 | - Makes possible to represent data with truly variable properties 126 | 127 | --- 128 | 129 | ## Simple query example 130 | 131 | ```js 132 | db.players.find({ 133 | _id: "57ade8face4bf5361c4268d6" 134 | }); 135 | 136 | db.players.find({ 137 | Score: 7, 138 | IsBanned: false 139 | }); 140 | ``` 141 | 142 | --- 143 | 144 | ## Use cases 145 | 146 | Games 147 | 148 | Web applications 149 | 150 | Analytics 151 | 152 | Logging applications 153 | 154 | Capturing data of which structure can’t be known in advance 155 | 156 | As a medium grade cache 157 | 158 | Note: 159 | 160 | MongoDB’s relevance to analytics derives from its speed and from two key features: targeted atomic updates and capped collections 161 | 162 | Atomic updates let clients efficiently increment counters and push values onto arrays 163 | 164 | Capped collections, often useful for logging, feature fixed allocation, which lets them age out automatically 165 | 166 | --- 167 | 168 | ## Why to use MongoDB 169 | 170 | Intuitive data model 171 | 172 | - Represent rich hierarchical data structures without complicated multi-table joins 173 | - Data can be persisted “as is” without the need for complexity of object mappers 174 | 175 | Relatively easy to scale horizontally 176 | 177 | Sophisticated query system 178 | 179 | Schemaless data model 180 | 181 | Note: 182 | 183 | - In fully normalized relational database the data for single query might be spread across dozens of tables 184 | 185 | --- 186 | 187 | ## When not to use MongoDB 188 | 189 | When there is a need for proper transactions 190 | 191 | - MongoDB can do them only at document level 192 | 193 | Joining many different types of data across many different dimensions 194 | 195 | In shared environments 196 | 197 | - Memory for the database will be allocated automatically as needed 198 | 199 | --- 200 | 201 | ## The core server 202 | 203 | The core database server runs via an executable called mongod (mongodb.exe on Windows) 204 | 205 | All the data files for a mongod process are stored by default in /data/db (c:\data\db) 206 | 207 | Simple to configure (a design feature – not a bug) 208 | 209 | The mongod server process receives commands over a network socket using a custom binary protocol 210 | 211 | Note: 212 | 213 | Database tuning, which in most RDBMSs means tinkering with a wide array of parameters controlling memory allocation and the like, has become something of a black art 214 | 215 | MongoDB’s design philosophy dictates that memory management is better handled by the operating system than by a DBA or application developer 216 | 217 | --- 218 | 219 | ## The JavaScript shell 220 | 221 | Runs via executable called mongo (.exe) 222 | 223 | Tool for administering the database and manipulating data 224 | 225 | Connects to a specified mongod process 226 | 227 | Most commands are issued using JavaScript expressions 228 | 229 | Shell is also a JavaScript interpreter so code like this can be run: 230 | 231 | ```js 232 | for (i = 0; i < 200000; i++) { 233 | db.numbers.save({ num: i }); 234 | } 235 | ``` 236 | 237 | --- 238 | 239 | ## Basic Administration 240 | 241 | `show dbs` lists all databases on the system 242 | 243 | `show collections` displays a list of all the collections defined in the current database 244 | 245 | `db.stats()` and `db.collection.stats()` display lower-level insight into databases and collections 246 | 247 | `db.help()` prints list of commonly used methods for operation on database objects 248 | 249 | `db.foo.help()` prints list of methods for collections 250 | 251 | Note: 252 | 253 | - Some of the values provided in these result documents are useful only in complicated debugging situations 254 | 255 | --- 256 | 257 | ## Nuts and bolts 258 | 259 | --- 260 | 261 | ### Documents 262 | 263 | All documents are serialized to BSON before being sent to MongoDB 264 | 265 | It’s important to consider the length of the key names since they are stored in to documents 266 | 267 | Documents are limited to 16 MB in size 268 | 269 | - To prevent developers from doing bad designs 270 | - For performance reasons 271 | 272 | --- 273 | 274 | ### Documents 275 | 276 | Can store: 277 | 278 | - Simple datatypes such as strings, numbers and dates 279 | - Arrays 280 | - Other documents 281 | 282 | Note: 283 | 284 | In RDBMS, column names are always kept separate from the rows they refer to 285 | 286 | On the server side, querying a large document requires that the document be copied into a buffer before being sent to the client. This copying can get expensive, especially (as is often the case) when the client doesn’t need the entire document. In addition, once sent, there’s the work of transporting the document across the network and then deserializing it on the driver side. This can become especially costly if large batches of multimegabyte documents are being requested at once 287 | 288 | --- 289 | 290 | ### Collections 291 | 292 | Containers for structurally or conceptually similar documents 293 | 294 | Names can contain numbers, letters or ‘.’ characters 295 | 296 | A fully qualified collection name can’t be longer than 128 characters 297 | 298 | Note: 299 | 300 | ‘.’ character can be used for creating virtual namespaces but it’s just an organizational principle 301 | 302 | MongoDB also uses some collections internally: system.namespaces and system.indexes 303 | 304 | Can be renamed 305 | 306 | --- 307 | 308 | ### Databases 309 | 310 | Logical and physical grouping of collections 311 | 312 | Automatically created when you first write to the collection for the first time 313 | 314 | Every collection and index are stored into its own namespace, and the metadata for each is stored in a file which size can’t exceed 16MB. 315 | 316 | - In practice the file can store approximately 24 000 collections/indexes 317 | 318 | Database file start from 64 MB and each new file is twice the size of the previous one until the size is 2 GB 319 | 320 | Note: 321 | 322 | - You’re not likely to need anywhere close to 24 000 collections 323 | - If really needed, the file can be made larger with –nssize server option 324 | - A common allocation strategy 325 | - There is –noprealloc and –smallfiles server options if the disk space is limited 326 | - db.stats() can be used for seeing stats from disk usage 327 | - A database can be dropped with a drop database –command which can not be undone 328 | - MongoDB uses preallocation to ensure that as much data as possible will be stored contiguously 329 | 330 | --- 331 | 332 | ## Inserting documents 333 | 334 | ```C# 335 | await _collection.InsertOneAsync(player); 336 | ``` 337 | 338 | Inserts the object as a document 339 | 340 | If the \_id field is not passed, it will be automatically generated 341 | 342 | --- 343 | 344 | ## Reading documents 345 | 346 | ```C# 347 | FilterDefinition filter = Builders.Filter.Eq(p => p.Score, 5); 348 | Player player = await _collection.Find(filter).FirstAsync(); 349 | ``` 350 | 351 | Find returns the documents matching the filter 352 | 353 | FirstAsync is used to stop the query to the first match 354 | 355 | --- 356 | 357 | ### Updating documents 358 | 359 | ```C# 360 | FilterDefinition filter = Builders.Filter.Eq(p => p.Id, player.Id); 361 | await _collection.ReplaceOneAsync(filter, player); 362 | ``` 363 | 364 | First parameter is the query for finding the document to replace 365 | 366 | Replaces all the values in the document are replaced with the properties in the `Player` 367 | 368 | --- 369 | 370 | ### Deleting documents 371 | 372 | ```C# 373 | FilterDefinition filter = Builders.Filter.Eq(p => p.Id, playerId); 374 | await _collection.FindOneAndDeleteAsync(filter); 375 | ``` 376 | 377 | All documents matching the filter will be removed 378 | -------------------------------------------------------------------------------- /presentations/mongodb_2.md: -------------------------------------------------------------------------------- 1 | # MongoDB: most important Queries, Updates and Aggregration 2 | 3 | --- 4 | 5 | ## Queries 6 | 7 | MongoDB has its own JSON-like query language 8 | 9 | You can perform ad hoc queries on the database using the find or findOne functions 10 | 11 | **Query selectors** can be used to match documents 12 | 13 | - You can query for ranges, set inclusion, inequalities and more 14 | 15 | **Query options** can be used to modify the result of the queries 16 | 17 | - sorting, choosing which fields to include etc… 18 | 19 | Note: 20 | 21 | - Regular expressions can be used for queries and they can take advantage of indexes 22 | - Every MongoDB query is fundamentally a instantiation of a cursor and fetching of that cursor’s result set 23 | 24 | --- 25 | 26 | ### Queries: Find method 27 | 28 | The "find" method is used to perform queries 29 | 30 | Querying returns a subset of documents in a collection 31 | 32 | Takes a query document for filtering 33 | 34 | Empty query document matches everything in the collection 35 | 36 | --- 37 | 38 | ### Queries: Find method 39 | 40 | Example in C#: 41 | 42 | ```C# 43 | FilterDefinition filter = 44 | Builders.Filter.Eq(p => p.Name, name); 45 | 46 | Player player = 47 | await collection.Find(filter).FirstAsync(); 48 | ``` 49 | 50 | --- 51 | 52 | ### Query cursor 53 | 54 | When you call find, the database is NOT queried immediately 55 | 56 | The database returns the results from "find" by using a cursor 57 | 58 | Cursor allows control of the eventual output of a query 59 | 60 | Almost every method on a cursor object returns the cursor itself 61 | 62 | - It's possible chain options in any order 63 | 64 | --- 65 | 66 | ### Query cursor example 67 | 68 | ```C# 69 | SortDefinition sortDef = 70 | Builders.Sort.Ascending("Level"); 71 | 72 | IFindFluent cursor = 73 | collection.Find("") 74 | .Sort(sortDef) 75 | .Limit(1) 76 | .Skip(10); 77 | ``` 78 | 79 | Query is executed when results are requested from the cursor: 80 | 81 | ```C# 82 | List players = await cursor.ToListAsync(); 83 | ``` 84 | 85 | --- 86 | 87 | ### Query selectors: Comparisons 88 | 89 | **\$lt** == lower than (<) 90 | 91 | **\$lte** == lower than or equal (<=) 92 | 93 | **\$gt** == greater than (>) 94 | 95 | **\$gte** == greater than or equal (>=) 96 | 97 | Example in C#: 98 | 99 | ```C# 100 | FilterDefinition filter = 101 | Builders.Filter.Gte("Level", 18) 102 | & Builders.Filter.Lte("Level", 30); 103 | 104 | List players = 105 | await collection.Find(filter).ToListAsync(); 106 | ``` 107 | 108 | Note: 109 | 110 | - Range queries will match only if they have same type as the value to be compared against 111 | - Generally you should not store more than one type for key within the same collection 112 | - (e.g. db.players.find({score: {$gte: 100, $lte: 200}}) 113 | 114 | --- 115 | 116 | ### Query selectors: Set operators 117 | 118 | Each takes a list of one or more values as predicate 119 | 120 | **\$all** – returns a document if all of the given values match the search key 121 | 122 | **\$in** - returns a document if any of the given values matches the search key 123 | 124 | **\$nin** – returns a document if none of the given values matches the search key 125 | 126 | ```C# 127 | var filter = 128 | Builders.Filter.In( 129 | p => p.Level, 130 | new[] { 10, 20, 30 } 131 | ); 132 | ``` 133 | 134 | Note: 135 | 136 | - When using the set operators, keep in mind that $in and $all can take advantage of indexes, but $nin can’t and thus requires a collection scan. If you use $nin, try to use it in combination with another query term that does use an index. 137 | - Make example of using set 138 | 139 | --- 140 | 141 | ### Query selectors: Boolean operators (1) 142 | 143 | **\$ne** – not equal 144 | 145 | **\$not** – negates the result of another MongoDB operator or regular expression query 146 | 147 | **\$exists** – used for querying documents containing a particular key, needed because collections don’t enforce schema 148 | 149 | Note: 150 | 151 | - \$ne can not take advantage of indexes 152 | - Do not use $not if there exists a negated form for the operator (e.g. $in and \$nin) 153 | - If possible values are scoped to the same key, use \$in instead 154 | 155 | --- 156 | 157 | ### Query selectors: Boolean operators (2) 158 | 159 | **\$or** – expresses a logical disjunction of two values for two different keys 160 | 161 | **\$and** – both selectors match for the document 162 | 163 | ```C# 164 | var f1 = Builders.Filter.Eq(p => p.Name, "John"); 165 | var f2 = Builders.Filter.Gt(p => p.Level, 10); 166 | var andFilter = Builders.Filter.And(f1, f2); 167 | ``` 168 | 169 | Note: 170 | 171 | - $and and $or take array of query selectors, where each selector can be arbitrarily complex and may itself contain other query operators. 172 | - MongoDB interprets all query selectors containing more than one key by ANDing the conditions, you should use \$and only when you can’t express an AND in a simpler way. 173 | 174 | --- 175 | 176 | ### Query selectors: Javascript 177 | 178 | If you can’t express your query with the tools described thus far, you have an option to write some JavaScript 179 | 180 | **\$where** operator is used to pass a JavaScript expression 181 | 182 | Adds substantial overhead because expressions need to be evaluated within a JavaScript interpreter context 183 | 184 | JavaScript expressions can’t use an index 185 | 186 | For security, use of "\$where" clauses should be highly restricted or eliminated 187 | 188 | Note: 189 | 190 | - Using where makes it possible to do JavaScript injection attacks – malicious users can’t write or delete this way but they might be able to read sensitive data 191 | 192 | --- 193 | 194 | ### Querying Arrays 195 | 196 | Querying for matching value in array uses the same syntax as querying a single value 197 | 198 | This, for example, would find a player with Tags “active” and “novice” 199 | 200 | ```C# 201 | var filter = 202 | Builders.Filter.Eq(p => p.Tags, "active"); 203 | 204 | var player = 205 | _collection.Find(filter).FirstAsync(); 206 | ``` 207 | 208 | **\$size** – allows to query for an array by its size 209 | 210 | --- 211 | 212 | ### Querying sub-documents 213 | 214 | **\$elemMatch** can be used to perform queries on the subdocuments 215 | 216 | ```C# 217 | var playersWithWeapons = 218 | Builders.Filter.ElemMatch( 219 | p => p.Items, 220 | Builders.Filter.Eq( 221 | i => i.ItemType, 222 | ItemType.Weapon 223 | ) 224 | ); 225 | ``` 226 | 227 | --- 228 | 229 | ### Query cursor options: **Projections** 230 | 231 | Query cursor options can be used to constrain the result set 232 | 233 | Returns only specific fields of the document 234 | 235 | ```C# 236 | var projection = 237 | Builders.Projection.As(); 238 | 239 | var limitedPlayer = 240 | await _collection.Find(filter).Project(projection).FirstAsync(); 241 | ``` 242 | 243 | --- 244 | 245 | ### Query cursor options: **Sorting** 246 | 247 | Can be used for sorting any result in ascending or descending order 248 | 249 | Can be used with one or more fields 250 | 251 | ```C# 252 | SortDefinition sortDef = 253 | Builders.Sort.Ascending("Level"); 254 | 255 | IFindFluent cursor = 256 | collection.Find("").Sort(sortDef); 257 | ``` 258 | 259 | --- 260 | 261 | ### Query cursor options: **Skip and limit** 262 | 263 | - Skip documents and limit documents returned by the query 264 | - Beware of skipping large number of documents because it gets ineffective 265 | 266 | ```C# 267 | IFindFluent cursor = 268 | collection.Find("").Limit(1).Skip(10); 269 | ``` 270 | 271 | Note: 272 | 273 | - Especially in cases where you have large documents, using a projection will minimize the costs of network latency and deserialization. 274 | 275 | --- 276 | 277 | ## Updates 278 | 279 | --- 280 | 281 | ### Updates 282 | 283 | Documents can be replaced completely 284 | 285 | Specific fields can be updated independently 286 | 287 | Targeted updates generally give better performance 288 | 289 | - Update documents are smaller 290 | - No need to fetch the existing document 291 | 292 | --- 293 | 294 | ### Updates 295 | 296 | By default update only updates the first document matched by its query selector 297 | 298 | - There is an option to do multidocument updates as well 299 | 300 | MongoDB supports upserts 301 | 302 | - Upsert means doing an insert if the document doesn’t exist and doing update if it already exists 303 | 304 | Note: 305 | 306 | - It’s best to keep the documents small if you intend to do a lot of updates 307 | - Updates that modify the structure and size of the document lead into rewriting the whole document (if the document size is several MBs, the updates will start getting costly) 308 | 309 | --- 310 | 311 | ### Standard update operators 312 | 313 | **\$inc** – increment or decrement a numeric value 314 | 315 | - Can be used with an upsert 316 | 317 | **\$set** – set value of any particular key to any valid BSON type 318 | 319 | **\$unset** – remove a key from the document 320 | 321 | - For an array, the value is merely set to null 322 | 323 | **\$rename** – change the name of a key 324 | 325 | - Works with subdocuments as well 326 | 327 | --- 328 | 329 | ### Array update operators 330 | 331 | **\$push** and **\$pushAll** – append value/list of values to an array 332 | 333 | **\$addToSet** – only adds the value if it doesn’t exist in the array 334 | 335 | - Can be used in combination with \$each to add multiple values 336 | 337 | **\$pop** – Used for removing an item from an array based on position, does not return removed value 338 | 339 | **\$pull** and **\$pullAll** – remove value/list of values by the value 340 | 341 | Note: 342 | 343 | - Since arrays are so central to MongoDB’s document model, there exists update operators just for arrays 344 | 345 | --- 346 | 347 | ### Atomic operations 348 | 349 | **findAndModify** –command 350 | 351 | Any one document can be updated atomically 352 | 353 | Document structure in itself makes it possible to fit within a single document many things that might atomic processing 354 | 355 | When updating and removing documents a special option called \$atomic can be used to make sure that the whole operation is performed before others read the touched documents 356 | 357 | Note: 358 | 359 | - Guarantee of consistent reads along a single connection 360 | - Database permits only one writer or multiple readers at once (not both) 361 | - Fetching data from disk yields the execution 362 | - Multidocument operations yield the execution unless explicitly specified not to 363 | 364 | --- 365 | 366 | ## Aggregation 367 | 368 | Summarizing and reframing the data 369 | 370 | Note: 371 | 372 | - Queries allow to get data as it’s stored -- Aggregations operations process data records and return computed results 373 | - Aggregation operations group values from multiple documents together, and can perform a variety of operations on the grouped data to return a single result 374 | - Aggregation = crunching 375 | 376 | --- 377 | 378 | ### Aggregation 379 | 380 | Simplified process of aggregation works like this: 381 | 382 | - Group values from multiple documents 383 | - Perform a variety of operations on the values 384 | - Return a single result 385 | 386 | Three methods for aggregation: 387 | 388 | **Aggregation pipeline** 389 | 390 | **Map-reduce** 391 | 392 | **Single purpose aggregation methods** 393 | 394 | --- 395 | 396 | ### Aggregation example description 397 | 398 | Find out what are the most common level for the players 399 | 400 | 1. Project the levels out of each player document 401 | 2. Group the levels by the number, counting the number of occurrences 402 | 3. Sort the levels by the occurrence count, descending 403 | 4. Limit the results to the first three 404 | 405 | --- 406 | 407 | ### Aggregation example 408 | 409 | ```C# 410 | var levelCounts = 411 | _collection.Aggregate() 412 | .Project(p => p.Level) 413 | .Group(l => l, p => new LevelCount { Id = p.Key, Count = p.Sum() }) 414 | .SortByDescending(l => l.Count) 415 | .Limit(3); 416 | ``` 417 | 418 | Note: 419 | 420 | - Project: The syntax is similar to the field selector used in querying: you can select fields to project by specifying " fieldname" : 1 or exclude fields with " fieldname" : 0. After this operation, each document in the results looks like: {"\_id" : id, "author" : " authorName"}. These resulting documents only exists in memory and are not written to disk anywhere 421 | - Group: This groups the authors by name and increments "count" for each document an author appears in. First, we specify the field we want to group by, which is "author" . This is indicated by the "\_id" : "$author" field. You can picture this as: after the group there will be one result document per author, so "author" becomes the unique identifier("_id" ). The second field means to add 1 to a "count" field for each document in the group. Note that the incoming documents do not have a "count" field; this is a new field created by the "$group" 422 | 423 | --- 424 | 425 | ## Aggregation pipeline 426 | 427 | Transform and combine documents in a collection 428 | 429 | Objects are transformed as they pass through a series of pipeline operators 430 | 431 | - Such as filtering, grouping and sortingg 432 | 433 | Pipeline operators need not produce one output document for every input document: operators may 434 | also generate new documents or filter out documents 435 | 436 | --- 437 | 438 | ## Aggregation pipeline 439 | 440 | Using projections you can 441 | 442 | - Add computed fields 443 | - Create new virtual sub-objects 444 | - Extract sub-fields into the top-level of results 445 | 446 | Pipeline operators can be repeated and combined freely 447 | 448 | Note: 449 | 450 | Returns result set inline 451 | 452 | Operations can be repeated, for example, multiple $project or $group steps. 453 | 454 | Supports non-sharded and sharded input collections 455 | 456 | Can be indexed and further optimized 457 | 458 | --- 459 | 460 | ### Aggregation pipeline operators 461 | 462 | **\$match** 463 | 464 | Filters documents so that you can run an aggregation on a subset of documents 465 | Can use all of the usual query operators 466 | 467 | **\$sort** 468 | 469 | Similar as sort with normal querying 470 | 471 | --- 472 | 473 | ### Aggregation pipeline operators 474 | 475 | **\$limit** 476 | 477 | Takes a number, n, and returns the first n resulting documents 478 | 479 | **\$skip** 480 | 481 | Takes a number, n, and discards the first n documents from the result set 482 | 483 | --- 484 | 485 | ### Aggregation pipeline operators 486 | 487 | **\$unwind** 488 | 489 | Unwinding turns each field of an array into a separate document 490 | 491 | ```C# 492 | var allItems = 493 | await _collection.Aggregate() 494 | .Unwind(p => p.Items) 495 | .As() 496 | .ToListAsync(); 497 | ``` 498 | 499 | --- 500 | 501 | ### Aggregation pipeline: **\$group** 502 | 503 | Grouping allows you to group documents based on certain fields and combine their values 504 | 505 | When you choose a field or fields to group by, you pass it to the \$group function as the group’s "\_id" field 506 | 507 | ```C# 508 | _collection.Aggregate() 509 | .Project(p => p.Level) 510 | .Group(l => l, 511 | p => new LevelCount { 512 | Id = p.Key, 513 | Count = p.Sum() 514 | } 515 | ) 516 | ``` 517 | 518 | --- 519 | 520 | ### Aggregation pipeline: **\$group** 521 | 522 | There are several operators which can be used with **\$group**, such as: 523 | 524 | - \$sum 525 | - \$max 526 | - \$min 527 | - \$first 528 | - \$last 529 | 530 | --- 531 | 532 | ### Aggregation pipeline: **\$project** 533 | 534 | Much more powerful in the pipeline than it is in the “normal” query language 535 | 536 | Allows to extract fields from subdocuments, rename fields, and perform operations on them 537 | 538 | --- 539 | 540 | ### Aggregation pipeline: \$project 541 | 542 | There are many expressions which can be applied when projecting 543 | 544 | - Math expressions - $add, $subtract, \$mod… 545 | - Date expressions - $year, $month, \$week… 546 | - String expressions - $subsctr, $concat, \$toLower… 547 | - Logical expressions - $cmp, $eq, \$gt… 548 | 549 | --- 550 | 551 | ### Map-reduce 552 | 553 | Can solve that is too complex for the aggregation framework 554 | 555 | Uses JavaScript as its “query language” so it can express arbitrarily complex logic 556 | 557 | Tends to be fairly slow and should not be used for real-time data analysis 558 | 559 | Somewhat complex to use 560 | 561 | --- 562 | 563 | ### Single purpose aggregation methods: **Count** 564 | 565 | ```C# 566 | var count = 567 | await _collection.CountAsync( 568 | Builder.Filter.Eq(p => p.Level, 3)); 569 | ``` 570 | 571 | Returns the count of documents qualifying the defined condition(s) 572 | 573 | --- 574 | 575 | ### Single purpose aggregation methods: **Distinct** 576 | 577 | The most simple way of getting distinct values for a key 578 | 579 | Works for array keys and single value keys 580 | 581 | ```C# 582 | var distinctLevels = 583 | await _collection.DistinctAsync( 584 | p => p.Level, 585 | Builders.Filter.Empty); 586 | ``` 587 | -------------------------------------------------------------------------------- /presentations/nosql.md: -------------------------------------------------------------------------------- 1 | # NoSQL 2 | 3 | --- 4 | 5 | ## History 6 | 7 | ![History](/resources/nosql-history.png) 8 | 9 | --- 10 | 11 | ## Application integration 12 | 13 | ![Integration](/resources/nosql-app-integration.png) 14 | 15 | Note: 16 | 17 | - Explain integration database 18 | - Explain why not a problem anymore 19 | 20 | --- 21 | 22 | ## Definition 23 | 24 | Not any specific technology or theory – concepts that have evolved over many years 25 | 26 | Ideas were adopted by independent groups of people who used them to solve their own data problems 27 | 28 | By now, the NoSQL movement includes hundreds of database products – most of them solve a very specific set of problems 29 | 30 | --- 31 | 32 | ## History 33 | 34 | What changed: rise of the internet and sites with lots and lots of traffic 35 | 36 | Google’s and Amazon’s technologies inspired a new movement: NoSQL 37 | 38 | Original spark was the need for clustering 39 | 40 | Note: 41 | 42 | - Examples: Amazon, Google and Ebay 43 | - How do you scale: buy more expensive hardware 44 | - Companies like google use different approach: lots of little boxes 45 | - RBDMS was designed to run on big boxes 46 | - Clustering RMBDMS is very hard to do; unnatural 47 | - Google (Bigtable) and Amazon (Dynamo) did their proprietary solutions 48 | - Beefing up hardware can become prohibitively costly when scaling vertically 49 | - Horizontally scaled data set can use commodity hardware 50 | - Distributing data across multiple machines mitigates risk of failing nodes 51 | 52 | --- 53 | 54 | ## Hardware 55 | 56 | ![Hardware](/resources/nosql-hardware.png) 57 | 58 | --- 59 | 60 | ## Definition 2 61 | 62 | Can not be defined 63 | 64 | Common characteristics: 65 | 66 | - Schema agnostic 67 | - Cluster friendly 68 | - Commodity hardware 69 | - Nonrelational 70 | - Open-source 71 | - 21st century web 72 | 73 | Note: 74 | 75 | - Open source is at the moment a common characteristic 76 | - Some databases are designed to operate best (or only) with specialized storage and processing hardware. With a NoSQL database, cheap off‐the‐shelf servers can be used. 77 | - Schema-less is not the whole truth. Application always defines an implicit schema (schema on read). 78 | - Explain: Why no schema makes the development faster 79 | 80 | --- 81 | 82 | ## Data models 83 | 84 | ![Data models](/resources/nosql-data-models.png) 85 | 86 | Note: 87 | 88 | - Line between document and key-value stores is fuzzy 89 | - Aggregated oriented put data into bigger chunks 90 | - Graph oriented make the data into even smaller units 91 | - Graph example: neo4j 92 | - Key-value: Just a hashmap but persisted on a disc 93 | - Columnar: Cassandra, which implements many of Dynamo’s scaling properties while providing a column-oriented data model inspired by Google’s BigTable. Cassandra is an open source version of a data store built by Facebook for its inbox search feature. The system scaled horizontally to index more than 50 TB of inbox data, allowing for searches on inbox keywords and recipients. Data was indexed by user ID, where each record consisted of an array of search terms for keyword searches and an array of recipient IDs for recipient searches. 94 | 95 | --- 96 | 97 | ## Schema agnostic 98 | 99 | A database schema is the description of all possible data and data structures in a relational database 100 | 101 | With a NoSQL database (usually) a schema isn’t required 102 | 103 | Changing the structure of the data stored doesn’t need actions on a database level 104 | 105 | Application still needs to know how the data is stored when reading the data 106 | 107 | Note: 108 | 109 | - The application needs to take care of the changes to the schema when reading the data 110 | - Shortens develop time especially in an iterative project where the design evolves throughout development (exactly what games tend to be) 111 | 112 | --- 113 | 114 | ## Cluster friendly 115 | 116 | Many NoSQL databases are designed to be distributed on multiples computers 117 | 118 | Storing the data in natural aggregates is the way to make the clustering feasible 119 | 120 | Clustering makes adding _high availability_ easier 121 | 122 | Note: 123 | 124 | - Natural aggregate -> Storing the combination of data that is commonly accessed together 125 | - High availability means keeping the data accessible even when a limited subset of the database servers are not available 126 | - Graph databases don’t save the data in aggregates and are generally not distributable 127 | 128 | --- 129 | 130 | ## How the data is distributed: 131 | 132 | ![Data distribution](/resources/nosql-data-distribution.png) 133 | 134 | Note: 135 | 136 | - Two ways of distributing data: sharding and replication 137 | - You get same logical inconsistencies with sharding as with a single machine setup (exacerbated to some degree) 138 | - Draw the hotel booking example 139 | 140 | --- 141 | 142 | ## Nonrelational 143 | 144 | NoSQL databases don’t have the concept of relationships built in 145 | 146 | NoSQL compensates this partly by having broader variety of data structures available 147 | 148 | - Objects within objects, arrays etc… 149 | 150 | Note: 151 | 152 | - Relational views and NoSQL denormalization are different approaches to the problem of data spread across records 153 | - Explain why you need to be careful with denormalization (volational data) 154 | 155 | --- 156 | 157 | ## Nonrelational 158 | 159 | In relational databases the goal is to normalize the data (remove duplicate data) 160 | 161 | - Easy to update (updates only to one place) 162 | - Queries might get complex to implement and slow to execute 163 | 164 | In NoSQL data is often deliberately denormalized (stored multiple times) 165 | 166 | - Enables fast query speed and ease of query implementation 167 | - Updates have to be applied to all the places where a piece of data is stored 168 | 169 | --- 170 | 171 | ## Consistency 172 | 173 | Consistency is a property which defines how well the read data matches the written data 174 | 175 | There are two implementation models of consistency for NoSQL databases 176 | 177 | **ACID** Consistency (ACID stands for Atomicity, Consistency, Isolation, Durability) 178 | 179 | - Means that once data is written, reads are fully consistent 180 | 181 | **BASE** (Eventual Consistency) 182 | 183 | - Means that once data is written, it will eventually appear for reading 184 | 185 | Note: 186 | 187 | - There are two types of consistency: replication and logical consistency 188 | - You don’t want to run into a situation where someone else uses the data while you are in the middle of writing it 189 | - Draw on the board why you can’t do just ACID in certain big scale systems anyways 190 | 191 | --- 192 | 193 | ## Availability 194 | 195 | Availability is a property which defines how much of the data can be accessed at any given time 196 | 197 | - Regardless of other parties accessing the data 198 | - Regardless of connections getting lost between the servers 199 | - The data can be stale (not consistent) 200 | 201 | Generally NoSQL databases are built for high availability 202 | 203 | - High availability databases are built to eliminate single points of failure 204 | - Optimized to ensure that the end user does not experience an interruption in service 205 | 206 | Note: 207 | 208 | - Many NoSQL databases differentiate between read and write availability 209 | - Some allow only writes when the connection between cluster nodes is interrupted to prevent serving stale data – some allow only reads to prevent conflicts between writes 210 | 211 | --- 212 | 213 | ## CAP-theorem 214 | 215 | ![CAP](/resources/nosql-cap.png) 216 | 217 | --- 218 | 219 | ## CAP-theorem 220 | 221 | CAP stands for Consistency, Availability, and Partitioning 222 | 223 | States that you cannot have all three completely at the same time 224 | 225 | Many NoSQL databases allow tuning between levels of consistency and availability 226 | 227 | In clustered databases the partitioning is always present so the trade off needs to be made between consistency and availability 228 | 229 | - It’s a tunable sliding scale – not a binary decision 230 | 231 | --- 232 | 233 | ## When to use 234 | 235 | When you need one more of these: 236 | 237 | - Large scale data 238 | - Scale the system horizontally 239 | - An ability to adapt to changing business requirements quickly 240 | - NoSQL data structures such as arrays, sorted sets and graphs 241 | 242 | And when NoSQL can reduce the technical complexity of the system 243 | 244 | Note: 245 | 246 | - Relational databases often contain more features 247 | - NoSQL databases usually have less and are more specific to certain use cases 248 | - Writing SQL queries can get relatively complex when the simultaneously queried data needs to be split in many tables 249 | 250 | --- 251 | 252 | ## When not to use 253 | 254 | When you have data that is actually highly relational 255 | 256 | When you need ACID transactions 257 | 258 | When your team has strong relational database skills and it can get the job done 259 | 260 | --- 261 | 262 | ## Comparison 263 | 264 | | Relational | NoSQL | 265 | | ----------------------------------- | ----------------------------------------- | 266 | | Scaling vertically | Scaling horizontally | 267 | | Different products for same problem | Different products for different problems | 268 | | Proven track record of 30+ years | Products are less mature | 269 | | Query language: SQL | No standardized query language | 270 | | Data normalized | Data is often denormalized | 271 | | Has schema | Schema agnostic (on read schema) | 272 | -------------------------------------------------------------------------------- /presentations/rest.md: -------------------------------------------------------------------------------- 1 | # Just enough REST 2 | 3 | Note: 4 | 5 | - Following will explain what a service following the REST architectural style should look like to a caller 6 | - REST is effectively a restating or clarification of what an ideal web-based application should look like 7 | - For making programming interfaces 8 | - Meant to improve: performance, scalability, simplicity, ability to transform, movability and trustworthiness 9 | 10 | --- 11 | 12 | ## How does a RESTful API look like? 13 | 14 | Typical URIs: 15 | 16 | - GET /api/players/ 17 | - GET /api/players/{id} 18 | - POST /api/players/ 19 | - PUT /api/players/{id} 20 | - DELETE /api/players/{id} 21 | - GET /api/players/{id}/items/ 22 | 23 | https://dev.twitter.com/rest/public 24 | 25 | Note: 26 | 27 | - Typical methods are named with custom verbs, REST uses HTTP verbs 28 | - Technically speaking, the REST architectural style is agnostic about any specific protocol. That includes the HTTP protocol. - In other words, all you need is a protocol that provides a language and mechanism for describing both states (i.e., representations) and state changes. 29 | 30 | --- 31 | 32 | ## REST 33 | 34 | REST is an acronym for REpresentational State Transfer 35 | 36 | REST is based solely on existing HTTP standards 37 | 38 | Extremely interoperable across all platforms capable of making HTTP requests 39 | 40 | Note: 41 | 42 | - This not only includes computers, smartphones, and tablets, but it also gets into devices such as phone systems, ATM machines, refrigerators, alarm systems, browsers, smart watches, etc. As long as the device can make an HTTP request to a URL, it can “do” REST. 43 | 44 | --- 45 | 46 | ## REST architectural constraints 47 | 48 | The REST architectural style describes six constraints: 49 | 50 | - Client-Server architecture 51 | - Uniform interface 52 | - Stateless 53 | - Cacheable 54 | - Layered System 55 | - Code on Demand (optional) 56 | 57 | --- 58 | 59 | ### REST: Uniform interface 60 | 61 | Resources are in the center of the API 62 | 63 | Individual resources are identified in requests 64 | 65 | - URIs in Web-based REST systems: (…/api/players) 66 | 67 | --- 68 | 69 | ### REST: Uniform interface 70 | 71 | Resources themselves are conceptually separate from the representations that are returned to the client 72 | 73 | - Server may return data from database in XML or JSON format which is not the same as the internal representation 74 | 75 | When a client holds a representation of a resource, it has enough information to modify or delete the resource 76 | 77 | --- 78 | 79 | ### REST: Stateless 80 | 81 | Server doesn’t store any client context 82 | 83 | Each request from any client contains all the information necessary to service the request 84 | 85 | The session state can be transferred by the server to another service such as a database to maintain a persistent state 86 | 87 | HTTP is itself a stateless protocol 88 | 89 | --- 90 | 91 | ### REST: Layered System 92 | 93 | A client cannot ordinarily tell whether it is connected directly to the end server, or to an intermediary along the way 94 | 95 | Intermediary servers may improve system scalability by enabling load balancing and by providing shared caches 96 | 97 | Intermediary servers may also enforce security policies 98 | 99 | --- 100 | 101 | ### HTTP verbs (HTTP methods) 102 | 103 | HTTP verbs are used to act on or fetch information about resources 104 | 105 | RESTful applications should choose the verbs to use from the verbs available in the protocol 106 | 107 | - Typically all clients support GET and POST 108 | - Most also support PUT and DELETE 109 | - There are a few more less supported verbs: PATCH, HEAD, and OPTIONS 110 | 111 | Not all verbs need to be supported for each resource type 112 | 113 | Note: 114 | 115 | - The use of the Delete verb is also hotly debated. Most enterprise applications don’t allow the caller to really delete a resource which would cause the verb to go unused if used only for deletes 116 | - The debate over how the HTTP verbs are supposed to be used can be quite heated at times 117 | - The important thing is to conform as closely to HTTP standards as possible 118 | 119 | --- 120 | 121 | ### HTTP verbs: Expected actions 122 | 123 | | | (Collection URI) http://myserver.com/players | (Element URI) http://myserver.com/players/1234 | 124 | | ------ | --------------------------------------------------------------------------- | --------------------------------------------------------------- | 125 | | GET | List of players, including URIs to individual players | Get a specific player, identified by the URI | 126 | | PUT | Replace the entire collection of players | Replace or create the single players identified by the URI | 127 | | POST | Create a new single player, where its identifier is generated by the system | Create a new subordinate under the player identified by the URI | 128 | | DELETE | Delete the entire collection of players | Delete the players identified by the URI | 129 | 130 | Note: 131 | 132 | - The exact meaning of each of the four verbs is dependent on the URI (context) 133 | - Even though there are 4 verbs you can use, there are actually 8 different actions available 134 | 135 | --- 136 | 137 | ### HTTP verbs: Idempotence 138 | 139 | The GET, PUT, and DELETE methods are said to be idempotent 140 | 141 | - calling them over and over will produce the same result without any additional side effects. 142 | 143 | GET method should not change the system at all 144 | 145 | DELETE method should delete the resource if it exist and not give any errors if it does not 146 | 147 | --- 148 | 149 | ### HTTP verbs: Idempotence 150 | 151 | PUT method should update the resource if it already exists. Or, if it doesn’t exist, the system should create the resource as submitted. 152 | 153 | POST method is not idempotent, it should always result in a new resource instance for each call 154 | 155 | --- 156 | 157 | ### Errors 158 | 159 | Only the well-known set of HTTP status codes should be used 160 | 161 | API should give an useful error message for the developer 162 | 163 | - Should be in same format as any other resource received from the API 164 | - A good practice is to also include a more detailed description of the error in addition to the error message 165 | 166 | Note: 167 | 168 | When the application is in production, the error messages should not be so descriptive that they could reveal potential security holes 169 | 170 | --- 171 | 172 | ### HTTP status codes – some well known examples 173 | 174 | 200 - All is good 175 | 176 | 301 - The resource was moved 177 | 178 | 400 - Bad request 179 | 180 | 401 - Unauthorized 181 | 182 | 404 - Resource not found 183 | 184 | 409 - Conflict 185 | 186 | 500 - Server error 187 | 188 | Note: 189 | 190 | - You should definitely use at least these 191 | - If starts with 2 – Everything is alright 192 | - If starts with 3 – No errors but special action might need to be taken 193 | - If starts with 4 – User has a problem 194 | - If starts with 5 – Server has a problem 195 | 196 | --- 197 | 198 | ### Response format 199 | 200 | Responses are usually made in JSON or XML 201 | 202 | --- 203 | 204 | ### JSON 205 | 206 | JSON’s popularity is growing 207 | 208 | Less verbose (quicker to read and write) 209 | 210 | Very easy to use with JavaScript 211 | 212 | --- 213 | 214 | ### XML 215 | 216 | More extension possibilities 217 | 218 | More tools 219 | 220 | --- 221 | 222 | ### Result filtering, sorting & searching 223 | 224 | Filtering 225 | 226 | - Use a unique query parameter for each field that implements filtering 227 | - Example: GET /items?min_price=3 228 | 229 | Sorting 230 | 231 | - A generic parameter sort can be used to describe sorting rules 232 | - Example: GET /items?sort=-price 233 | 234 | --- 235 | 236 | ### Result filtering, sorting & searching 237 | 238 | Searching 239 | 240 | - Combine filters, sorts and even full text search 241 | - GET /tickets?q=return&state=open&sort=-priority,created_at 242 | 243 | To make the API more pleasant, commonly used conditions can be packaged into easily accessible RESTful paths 244 | 245 | - GET /tickets/recently_closed 246 | 247 | --- 248 | 249 | ### Overriding the HTTP method 250 | 251 | Some HTTP clients can only work with simple GET and POST requests (E.g. Unity’s Www-class) 252 | 253 | A popular convention is to accept a request header *X-HTTP-Method-Override* with a string value containing one of PUT, PATCH or DELETE 254 | 255 | Note that the override header should only be accepted on POST requests 256 | -------------------------------------------------------------------------------- /presentations/web_api_1.md: -------------------------------------------------------------------------------- 1 | # ASP.NET Core Web API 2 | 3 | --- 4 | 5 | ## Web API: what can we do with it? 6 | 7 | Slower paced (asynchronous) games 8 | 9 | Validating single player game sessions 10 | 11 | Supporting systems 12 | 13 | - account management, leaderboards, cloud-saves etc. 14 | 15 | Note: 16 | 17 | Not-so-great for creating real-time multiplayer games! 18 | 19 | --- 20 | 21 | ## ASP.NET Core 22 | 23 | ASP.NET Core is a framework for building web UI and web APIs 24 | 25 | Why ASP.NET Core? 26 | 27 | - Cross-platform 28 | - High-performance 29 | - Open-source framework 30 | - Many necessary features built-in such as logging, environment-based configuration, dependency injection etc. 31 | - Cloud-ready 32 | 33 | --- 34 | 35 | ## Before we get into the business… 36 | 37 | --- 38 | 39 | ### HTTP messaging 40 | 41 | HTTP is a communication protocol, that is used to deliver data on the World Wide Web 42 | 43 | HTTP is based on the client-server architecture model 44 | 45 | A stateless request/response protocol 46 | 47 | Operates by exchanging messages across a reliable TCP/IP connection 48 | 49 | --- 50 | 51 | ### HTTP messaging 52 | 53 | ![HTTP messaging](/resources/http-messaging.png) 54 | 55 | --- 56 | 57 | ### HTTP request 58 | 59 | HTTP request contains: 60 | 61 | - Protocol 62 | - Path 63 | - Query string 64 | - Headers 65 | - Body 66 | 67 | --- 68 | 69 | ### HTTP GET request example 70 | 71 | GET /hello.htm HTTP/1.1 72 | 73 | Host: www.somerandomadress.com 74 | 75 | Content-Type: application/json 76 | 77 | Accept-Language: en-us 78 | 79 | Accept-Encoding: gzip, deflate 80 | 81 | Connection: Keep-Alive 82 | 83 | --- 84 | 85 | ### HTTP POST request example 86 | 87 | POST /randomThings/ HTTP/1.1 88 | 89 | Host: www.somerandomadress.com 90 | 91 | Content-Type: application/json 92 | 93 | Content-Length: length 94 | 95 | Accept-Language: en-us 96 | 97 | Accept-Encoding: gzip, deflate 98 | 99 | Connection: Keep-Alive 100 | 101 | { licenseID: 123, content: "stuff"} 102 | 103 | --- 104 | 105 | ### HTTP response 106 | 107 | HTTP response contains: 108 | 109 | - Protocol 110 | - Path 111 | - Headers 112 | - Body 113 | - Status Code 114 | 115 | --- 116 | 117 | ### HTTP response example 118 | 119 | HTTP/1.1 400 Bad Request 120 | 121 | Date: Sun, 18 Oct 2017 10:36:20 GMT 122 | 123 | Server: Apache/2.2.14 (Win32) 124 | 125 | Content-Length: 230 126 | 127 | Content-Type: application/json 128 | 129 | Connection: Closed 130 | 131 | { message: "You did a bad request" } 132 | 133 | --- 134 | 135 | ## Now back to ASP.NET Core... 136 | 137 | --- 138 | 139 | ## ASP.NET Core processing pipeline 140 | 141 | --- 142 | 143 | ### Simplified processing pipeline 144 | 145 | ![ASP.NET Pipeline](/resources/aspnet-simple-pipeline.png) 146 | 147 | --- 148 | 149 | ## Controllers 150 | 151 | A controller is used to define and group a set of actions 152 | 153 | An action (or action method) is a method on a controller which handles requests 154 | 155 | Controller is an instantiable class in which at least one of the following conditions is true: 156 | 157 | - Name is suffixed with "Controller“ 158 | - Inherits from a class whose name is suffixed with "Controller" 159 | - Decorated with the [Controller] attribute 160 | 161 | --- 162 | 163 | ### Controller responbilities 164 | 165 | In well-factored apps, controller does not directly include data access or business logic 166 | 167 | - Controller delegates to services handling these responsibilities 168 | 169 | Responsibilities: 170 | 171 | - Ensure request data is valid 172 | - Choose which result for an API should be returned 173 | 174 | Note: 175 | 176 | Explain model binding 177 | 178 | - there is no contract between client and server 179 | - convention over configuration 180 | - Foundational principle in REST 181 | 182 | Allows service to benefit from model binding, controller-specific and action-specific filters, and result conversion 183 | 184 | Actions can return anything 185 | 186 | - frequently return an instance of IActionResult (or `Task` for async methods) that produces a response 187 | 188 | --- 189 | 190 | ## Routing to Controller Actions 191 | 192 | Routing middleware is used to match the URLs of incoming requests and map them to actions 193 | 194 | Routes are defined in startup code (convention based) or with attributes 195 | 196 | Both ways of routing can be mixed together 197 | 198 | If no routing attribute is defined, convention based routing is used 199 | 200 | --- 201 | 202 | ## Routing to Controller Actions 203 | 204 | Web API routes are often defined with attribute route 205 | 206 | - There is also convention based routing but we skip it for now 207 | 208 | Attribute routing is enabled when app.UseRouting(…) is called in the Startup.cs -file 209 | 210 | --- 211 | 212 | ### Attribute routing 213 | 214 | Example: 215 | 216 | ```C# 217 | [Route("players/{id}/items")] 218 | public Item[] GetItemsByPlayer(int id) { ... } 219 | ``` 220 | 221 | Route attribute are used to map actions directly to route templates 222 | 223 | Parameters are defined inside “{}” in the route 224 | 225 | - Web API tries to bind URI parameter -> method parameter 226 | 227 | Route can also use Http[verb]Attributes: 228 | 229 | ```C# 230 | [HttpPost("/players")] 231 | public Player AddPlayer() { ... } 232 | ``` 233 | 234 | --- 235 | 236 | ### Attribute routing: combining routes 237 | 238 | Route attributes on the controller are combined with route attributes on the individual actions 239 | 240 | - Makes routing less repetitive 241 | 242 | ```C# 243 | [Route("players")] 244 | public class PlayersController : Controller { 245 | 246 | [HttpGet] // Matches '/players' 247 | public IActionResult List() { ... } 248 | 249 | [HttpPost("{id}")] // Matches '/players/{id}' 250 | public IActionResult Edit(int id) { ... } 251 | } 252 | ``` 253 | 254 | --- 255 | 256 | ### Route constraints 257 | 258 | Define how the parameters in the route templates are matched 259 | 260 | The general syntax is “{parameter:constraint}”: 261 | 262 | ```C# 263 | [Route("players/{id:int}"] 264 | public Player GetPlayerById(int id) { ... } 265 | [Route("players/{name}"] 266 | public Player GetPlayerByName(string name) { ... } 267 | ``` 268 | 269 | - If the id part of the URI is an integer, the first route will be used, otherwise the second one 270 | 271 | Multiple constraints can be combined: 272 | 273 | ```C# 274 | [Route("players/{id:int:min(1)}"] 275 | public Player GetPlayerById(int id) { ... } 276 | ``` 277 | 278 | --- 279 | 280 | ## Model binding 281 | 282 | Setting the values for the parameters when a action (method) on a controller is called 283 | 284 | URI parameters are matched by name to the action method parameters 285 | 286 | - Works with simple types (int, string, bool…), collections and classes 287 | 288 | Binding behavior can be customized using attributes: [FromHeader], [FromQuery], [FromRoute], [FromForm] and[FromBody] 289 | 290 | Note: 291 | 292 | Simple types include the .NET primitive types (int, bool, double, and so forth), plus TimeSpan, DateTime, Guid, decimal, and string, plus any type with a type converter that can convert from a string. 293 | 294 | Complex types are read using a media-type (MIME type) formatter 295 | 296 | A media type, also called a MIME type, identifies the format of a piece of data. In HTTP, media types describe the format of the message body. A media type consists of two strings, a type and a subtype. For example: text/html 297 | 298 | How to force web api to change its behavior: 299 | 300 | - Read complex type from the URI by adding [FromUri] attribute to the parameter 301 | - Read simple type from the body by adding [FromBody] attribute to the parameter 302 | - You can make Web API treat a class as a simple type by creating a TypeConverter and providing a string conversion. 303 | - You can make custom model binders and have access to things like the HTTP request, the action description, and the raw values from the route data 304 | 305 | --- 306 | 307 | ### Model binding: binding object 308 | 309 | Requirements for the class defining the model: 310 | 311 | - Public constructor 312 | - Public setter methods for the properties 313 | 314 | It’s advisable to use request body to pass objects when possible 315 | 316 | - Http verb GET does not allow request body 317 | 318 | --- 319 | 320 | ### Model binding: data formats 321 | 322 | Request data can come in a variety of formats 323 | 324 | - including JSON, XML and many others 325 | 326 | ASP.NET Core Web API uses a configured set of formatters to handle the request data based on its content type 327 | 328 | - Supports only JSON by default 329 | 330 | --- 331 | 332 | ## Returning data from controller 333 | 334 | Controller action can return any of the following: 335 | 336 | - Void 337 | - Any simple type (int, string etc…) 338 | - Any type used for data transferring 339 | - A class implementing IActionResult 340 | 341 | --- 342 | 343 | ### IActionResult 344 | 345 | There are many predefined classes implementing **IActionResult** such as 346 | 347 | - OkResult 348 | - OkObjectResult 349 | - NotFoundResult 350 | - BadRequestResult 351 | 352 | Action results define the body of the response and http status code 353 | 354 | Note: 355 | 356 | Action results are great for separating the common logic of creating responses to different classes 357 | 358 | Makes the intent of the controller action clearer, by hiding the low-level details of constructing the response 359 | 360 | --- 361 | 362 | ## Dependency injection 363 | 364 | --- 365 | 366 | ### Dependency injection 367 | 368 | Software design pattern that implements inversion of control for resolving dependencies 369 | 370 | Dependency Inversion Principle: _“high level modules should not depend on low level modules; both should depend on abstractions”_ 371 | 372 | Instead of referencing specific implementations, classes request abstractions (typically interfaces) which are provided to them when the class is constructed 373 | 374 | --- 375 | 376 | ### Dependency injection: Implementation 377 | 378 | Classes request their dependencies via their constructor (or properties and methods) 379 | 380 | ```C# 381 | public class PlayersController : Controller { 382 | public PlayersController(IPlayerRepository repository) { ...} 383 | } 384 | ``` 385 | 386 | There is usually a class which creates all of the required dependencies for the application 387 | 388 | - This class is called Inversion of Control (IoC) container or Dependency Injection (DI) container 389 | 390 | ASP.NET Core has a built-in DI-container 391 | 392 | Note: 393 | 394 | If the controllers are going to do anything useful, and if they are going to be implemented in a well-architected manner using SOLID design principles, they will depend heavily upon functionality provided by other classes. 395 | 396 | Includes even seemingly harmless classes such as System.DateTime, System.IO.File, System.Environment 397 | 398 | A single container instance must meet these criteria: 399 | 400 | - Be created early in the application start-up process 401 | - Be available at all times while the application is running 402 | - Be destroyed as one of the lst steps the application takes during shutdown 403 | 404 | ASP.NET Core container supports only constructor injection by default 405 | 406 | --- 407 | 408 | ### Dependency injection: Implementation(2) 409 | 410 | The ConfigureServices method in the Startup class is responsible for defining the services the application will use 411 | 412 | ```C# 413 | public void ConfigureServices(IServiceCollection services){ 414 | services.AddDbContext( 415 | options => options.UseInMemoryDatabase()); // Add database context using extension method 416 | services.AddScoped(); // Register class PlayerRepository as IPlayerRepository 417 | } 418 | ``` 419 | 420 | IServiceCollection has methods **AddTransient**, **AddScoped** and **AddSingleton** for defining the lifetime for a service 421 | 422 | --- 423 | 424 | ### Dependency injection: service lifetimes - Transient 425 | 426 | Transient lifetime services are created each time they are requested 427 | 428 | This lifetime works best for lightweight, stateless services 429 | 430 | --- 431 | 432 | ### Dependency injection: service lifetimes - Scoped 433 | 434 | Scoped lifetime services are created once per request 435 | 436 | --- 437 | 438 | ### Dependency injection: service lifetimes - Singleton 439 | 440 | Singleton lifetime services are created the first time they are requested 441 | 442 | Every subsequent request will use the same instance 443 | 444 | --- 445 | 446 | ### The processing pipeline beyond the controller in _our_ Web API 447 | 448 | Controller -> Repository -> Database 449 | 450 | Controller has the following responsibilities: 451 | 452 | - Defining web API routes 453 | - Handling business logic 454 | 455 | Repository handles the data access logic 456 | 457 | Note: 458 | 459 | This is up to the application developer to decide 460 | 461 | Repository pattern: Essentially, it provides an abstraction of data, so that your application can work with a simple abstraction that has an interface approximating that of a collection. 462 | 463 | Business logic means applying business rules to the data (how it can be created, displayed, stored and changed) 464 | -------------------------------------------------------------------------------- /presentations/web_api_2.md: -------------------------------------------------------------------------------- 1 | # ASP.NET Core Web API part 2 2 | 3 | --- 4 | 5 | ## Simplified processing pipeline 6 | 7 | ![ASP.NET Pipeline](/resources/aspnet-simple-pipeline.png) 8 | 9 | --- 10 | 11 | ## Middleware 12 | 13 | The first stage in the processing pipeline 14 | 15 | Software that is assembled into an application pipeline to handle requests and responses 16 | 17 | Each middleware chooses whether to pass the request on or stop the processing 18 | 19 | Can perform certain actions before and after the next middleware is invoked in the pipeline 20 | 21 | --- 22 | 23 | ### Middleware 24 | 25 | ![ASP.NET Middleware](/resources/aspnet-middleware.png) 26 | 27 | --- 28 | 29 | ### Middleware: Example 30 | 31 | Here is the simplest possible middleware: 32 | 33 | ```C# 34 | public class Startup { 35 | public void Configure(IApplicationBuilder app) { 36 | app.Run(async context => { 37 | await context.Response.WriteAsync("Hello, World!"); 38 | }); 39 | } 40 | } 41 | ``` 42 | 43 | App.Run(…) ends the pipeline 44 | 45 | --- 46 | 47 | ### Middleware: configuration 48 | 49 | You can chain multiple request delegates together with app.Use 50 | 51 | ```C# 52 | public void Configure(IApplicationBuilder app) { 53 | app.Use(async (context, next) =>{ 54 | // Do work that doesn't write to the Response. 55 | await next.Invoke(); 56 | // Do other work that doesn't write to the Response. 57 | }); 58 | app.Run(async context => { 59 | await context.Response.WriteAsync("Hello, World!"); 60 | }); 61 | } 62 | ``` 63 | 64 | The next parameter represents the next delegate in the pipeline 65 | 66 | Middlewares are run in the registration order 67 | 68 | Note: 69 | 70 | You can end the pipeline by not calling next 71 | 72 | --- 73 | 74 | ### Middleware: configuration 75 | 76 | ![ASP.NET Middleware Configuration](/resources/aspnet-middleware-conf.png) 77 | 78 | --- 79 | 80 | ### Middleware: custom middleware 81 | 82 | ```C# 83 | public class TimeCounterMiddleware{ 84 | private readonly RequestDelegate _next; 85 | 86 | public TimeCounterMiddleware(RequestDelegate next){ 87 | _next = next; 88 | } 89 | 90 | public async Task Invoke(HttpContext context){ 91 | var watch = new Stopwatch(); 92 | watch.Start(); 93 | await _next(context); 94 | context.Response.Headers.Add("X-Processing-Time-Milliseconds", 95 | new[] { watch.ElapsedMilliseconds.ToString() }); 96 | } 97 | } 98 | ``` 99 | 100 | --- 101 | 102 | ### Middleware: custom middleware 103 | 104 | ```C# 105 | //in MiddlewareExtensions.cs 106 | public static class MiddlewareExtensions { 107 | public static IApplicationBuilder UseProcessingTimeCounterMiddleware(this IApplicationBuilder builder) { 108 | return builder.UseMiddleware(); 109 | } 110 | } 111 | 112 | // in Startup.cs 113 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { 114 | ... 115 | app.UseProcessingTimeCounterMiddleware(); // Could be also without the extension method: // app.UseMiddleware(); 116 | ... 117 | } 118 | ``` 119 | 120 | --- 121 | 122 | ## Error handling 123 | 124 | Requests that are not handled by your app will be handled by the server 125 | 126 | General error handling can be done in a middleware 127 | 128 | In ASP.NET Core Web API pipeline the exceptions can be handled with filters 129 | 130 | Note: 131 | 132 | There are two major cases for handling exceptions, the case where we are able to send an error response and the case where all we can do is log the exception 133 | 134 | --- 135 | 136 | ### Error handling middleware 137 | 138 | Should be the first middleware to guarantee that exceptions from other middlewares are caught 139 | 140 | There are many built-in middleware for handling errors such as 141 | 142 | - UseDeveloperExceptionPage() – Prints detailed error page with the call stack and much more 143 | - UseStatusCodePages() – Prints a friendly page defining the http status code 144 | - UseExceptionHandler() – Forwards the errored requests to another route 145 | 146 | You can also create your own error handling middleware 147 | 148 | --- 149 | 150 | ### Error handling middleware 151 | 152 | ```C# 153 | public class CustomExceptionHandlerMiddleware { 154 | private readonly RequestDelegate _next; 155 | public CustomExceptionHandlerMiddleware(RequestDelegate next) { 156 | _next = next; 157 | } 158 | public async Task Invoke(HttpContext context){ 159 | try { 160 | await _next(context) 161 | }; 162 | catch(Exception e){ 163 | //... 164 | } 165 | } 166 | } 167 | ``` 168 | -------------------------------------------------------------------------------- /presentations/web_api_3.md: -------------------------------------------------------------------------------- 1 | # ASP.NET Core Web API part 3 2 | 3 | --- 4 | 5 | ## Filters 6 | 7 | The filter pipeline runs after ASP.NET Web API selects the action to execute 8 | 9 | Filters are a way of implementing cross-cutting concerns 10 | 11 | - Avoiding duplicating code across actions 12 | 13 | There are built in filters such as: 14 | 15 | - Authorization 16 | - Ensuring HTTPS usage 17 | 18 | Possible filter scopes are global, controller and action 19 | 20 | Notes: 21 | 22 | Draw the entry to filter pipeline picture on the board 23 | 24 | --- 25 | 26 | ### Filters: Usage 27 | 28 | ```C# 29 | [Authorize] //built-in filter - applies to all actions in the controller 30 | public class SampleController : Controller { 31 | [OnlyTest] //custom filter - applies only to this action 32 | public string Test(){ 33 | return "Hello world!"; 34 | } 35 | } 36 | 37 | // In Startup.cs 38 | public void ConfigureServices(IServiceCollection services) { 39 | services.AddMvc(options => { 40 | options.Filters.Add(new SampleGlobalActionFilter()); // custom filter - applies to all controllers and their actions 41 | }); 42 | } 43 | ``` 44 | 45 | --- 46 | 47 | ### Filters: filter pipeline and filter types 48 | 49 | ![Filter pipeline](/resources/filter-pipeline.png) 50 | 51 | --- 52 | 53 | ### Filters: differences to middleware 54 | 55 | Most important is the execution order: 56 | 57 | - Filters run within the Web API filter pipeline 58 | - Middleware runs before Web API kicks in 59 | 60 | Filters can be specified per action – middleware only per URI 61 | 62 | Note: 63 | 64 | Filters can access the Web API context – Middleware can’t 65 | 66 | --- 67 | 68 | ### Exception filters 69 | 70 | The easiest solution for processing the subset of unhandled exceptions related to a specific action or a controller 71 | 72 | As with other filters, exception filter can be registered by action, by controller or globally 73 | 74 | ```C# 75 | public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute { 76 | public override void OnException(ExceptionContext context){ 77 | if (context.Exception is NotImplementedException){ 78 | context.Result = new NotFoundResult(); 79 | } 80 | } 81 | } 82 | ``` 83 | 84 | --- 85 | 86 | ## Model validation 87 | 88 | Before a server stores data in a database, the server must validate the data (model) 89 | 90 | - Check for potential security threats 91 | - Appropriately formatted 92 | - Conforms the rules of the application 93 | 94 | Model validation is done automatically when Controller is decorated with [ApiContoller] attribute 95 | 96 | - Returns HTTP Code 400 (Bad Request) when model is not valid 97 | 98 | Note: 99 | 100 | - Explain issues of under-posting and security threats for over-posting 101 | 102 | --- 103 | 104 | ### Model validation: built in attributes 105 | 106 | - [EmailAdress]: Validates the property has an email format 107 | - [Range]: Validates the property value falls within the given range 108 | - [Required]: Makes a property required 109 | - [StringLength]: Validates that a string property has at most the given maximum length 110 | 111 | ```C# 112 | public class Item{ 113 | public int Id { get; set; } 114 | [Required] 115 | public decimal? Price { get; set; } 116 | [Range(0, 99)] 117 | public double Weight { get; set; } 118 | } 119 | ``` 120 | 121 | --- 122 | 123 | ### Model Validation: Custom validation 124 | 125 | Custom validation attributes are also possible: 126 | 127 | ```C# 128 | public class UserOlderThan13Attribute : ValidationAttribute, IClientModelValidator { 129 | protected override ValidationResult IsValid(object value, ValidationContext validationContext) { 130 | Account account = (Account)validationContext.ObjectInstance; 131 | if (account.Age < 13) { 132 | return new ValidationResult(GetErrorMessage()); 133 | } 134 | return ValidationResult.Success; 135 | } 136 | ``` 137 | 138 | --- 139 | 140 | ## Logging 141 | 142 | Logging is a way of getting information out of application to the developers and system administrators 143 | 144 | Logging means writing information to an output such as: 145 | 146 | - Console 147 | - Text file 148 | - Network socket 149 | 150 | --- 151 | 152 | ### Logging 153 | 154 | Output format might need to be standard-based to enable easy parsing and processing 155 | 156 | ASP.NET Core supports a logging API that works with a variety of logging providers 157 | 158 | Built-in providers let you send logs to one or more destinations 159 | 160 | There are many third-party logging providers which can be used with ASP.NET Core 161 | 162 | You can also define you own providers 163 | 164 | --- 165 | 166 | ### Logging: Configuration 167 | 168 | ASP.NET Core dependency injection (DI) provides the ILoggerFactory instance 169 | 170 | The AddConsole and AddDebug extension methods call the ILoggerFactory.AddProvider method, passing in an instance of the provider 171 | 172 | ```C# 173 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) 174 | { 175 | loggerFactory 176 | .AddConsole() //built in provider which outputs to console 177 | .AddDebug(); //built in provider which outputs to debug stream 178 | ``` 179 | 180 | --- 181 | 182 | ### Logging: categories 183 | 184 | Each of the log messages has a category, the category can be any string 185 | 186 | - Convention is to use fully qualified name of the class: ”GameApi.Controllers.PlayersController” 187 | 188 | Easiest way to set the category is to request the generic ILogger in the class constructor which automatically sets the category 189 | 190 | ```C# 191 | private ILogger _logger; 192 | public PlayersController(ILogger logger) { 193 | _logger = logger; 194 | } 195 | ``` 196 | 197 | --- 198 | 199 | ### Logging: ASP.NET Core Log Levels 200 | 201 | Each time you write a log, you specify its Log Level 202 | 203 | The log level indicates the degree of severity or importance 204 | 205 | ILogger has extension methods for writing logs with each log level: 206 | 207 | \_logger.Information(...), \_logger.LogWarning(...), etc... 208 | 209 | --- 210 | 211 | ### Logging: ASP.NET Core Log Levels 212 | 213 | #### Trace = 0 214 | 215 | For information that is valuable only to a developer debugging an issue 216 | 217 | These messages may contain sensitive application data and so should not be enabled in a production environment 218 | 219 | --- 220 | 221 | ### Logging: ASP.NET Core Log Levels 222 | 223 | #### Debug = 1 224 | 225 | For information that has short-term usefulness during development and debugging 226 | 227 | You typically would not enable Debug level logs in production unless you are troubleshooting 228 | 229 | Example: “Entering method Configure with flag set to true” 230 | 231 | --- 232 | 233 | ### Logging: ASP.NET Core Log Levels 234 | 235 | #### Information = 2 236 | 237 | For tracking the general flow of the application 238 | 239 | These logs typically have some long-term value 240 | 241 | Example: “Request received for path /players” 242 | 243 | --- 244 | 245 | ### Logging: ASP.NET Core Log Levels 246 | 247 | #### Warning = 3 248 | 249 | For abnormal or unexpected events in the application flow 250 | 251 | These may include errors or other conditions that do not cause the application to stop, but which may need to be investigated 252 | 253 | Handled exceptions are a common place to use the Warning log level 254 | 255 | Example: ”FileNotFoundException for file game.txt” 256 | 257 | --- 258 | 259 | ### Logging: ASP.NET Core Log Levels 260 | 261 | #### Error = 4 262 | 263 | For errors and exceptions that cannot be handled 264 | 265 | These messages indicate a failure in the current activity or operation (such as the current HTTP request) 266 | 267 | Example: “Cannot insert record due to duplicate key violation” 268 | 269 | --- 270 | 271 | ### Logging: ASP.NET Core Log Levels 272 | 273 | #### Critical = 5 274 | 275 | For failures that require immediate attention 276 | 277 | Example: data loss scenario 278 | 279 | --- 280 | 281 | ## Application environments 282 | 283 | Environment is the place where the application lives 284 | There should be many environments for different versions of the application 285 | 286 | - Typically at least development, staging, and production 287 | 288 | Environments can be used for things like 289 | 290 | - Database connection information 291 | - Error pages (specific in development – vague in production) 292 | - Log-levels (verbose in development – only warnings and errors in production) 293 | 294 | --- 295 | 296 | ### Application environments 297 | 298 | ASP.NET Core provides support for controlling app behavior across multiple environments 299 | 300 | ASPNETCORE_ENVIRONMENT environment variable is used to decide the current environment for the application 301 | 302 | - The value can be anything but Development, Staging and Production are used commonly 303 | - You can set the variable on Windows cmd with “SET ASPNETCORE_ENVIRONMENT=Development” 304 | 305 | --- 306 | 307 | ### Application environments 308 | 309 | The IHostingEnvironment service provides the core abstraction for working with environments 310 | 311 | ```C# 312 | public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { 313 | if(env.IsDevelopment()) { ... } 314 | ``` 315 | 316 | The aim should be to have the environment checking –code in the startup class 317 | 318 | --- 319 | 320 | ### Application environments 321 | 322 | ASP.NET Core supports a convention-based approach to configuring an application's startup based on the current environment 323 | 324 | - if a class called Startup{EnvironmentName} (for example StartupDevelopment) exists and the environment name matches the value of ASPNETCORE_ENVIRONMENT, it is used instead of the normal Startup-class 325 | 326 | Configure() and ConfigureServices() –methods work with the same principle 327 | 328 | - You can use Configure{EnvironmentName}() and Configure{EnvironmentName}Services() 329 | -------------------------------------------------------------------------------- /reading/reading_week_1.md: -------------------------------------------------------------------------------- 1 | # Reading list week 1 2 | 3 | - Interfaces: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/ 4 | - Exception handling: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/exceptions/exception-handling 5 | - Creating and throwing exceptions: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/exceptions/creating-and-throwing-exceptions 6 | - Asynchronous programming: https://docs.microsoft.com/en-gb/dotnet/csharp/async 7 | - Generics: https://docs.microsoft.com/en-gb/dotnet/csharp/programming-guide/generics/ 8 | - Delegates: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/ 9 | - Lambda expressions: https://docs.microsoft.com/en-gb/dotnet/csharp/lambda-expressions -------------------------------------------------------------------------------- /reading/reading_week_2.md: -------------------------------------------------------------------------------- 1 | # Reading list week 2 2 | 3 | - Application startup: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-2.1 4 | - Controllers: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/actions?view=aspnetcore-2.1 5 | - Dependency injection: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1 6 | - Middleware: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1&tabs=aspnetcore2x 7 | - Routing to controller actions: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/routing?view=aspnetcore-2.1 8 | - Model binding: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.1 9 | - Repository pattern: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/repository-pattern?view=aspnetcore-2.1 -------------------------------------------------------------------------------- /reading/reading_week_3.md: -------------------------------------------------------------------------------- 1 | # Reading list week 3 2 | 3 | - Configuration: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-2.1&tabs=basicconfiguration 4 | - Middlewares: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.1&tabs=aspnetcore2x 5 | - Model validation: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.1 6 | - Filters: https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.1 7 | - Logging: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1 8 | - Environments: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-2. 9 | - Error handling: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-2.1 -------------------------------------------------------------------------------- /reading/reading_week_4.md: -------------------------------------------------------------------------------- 1 | # Reading list week 4 2 | 3 | Read more on how to use the MongoDb C# driver from here: http://mongodb.github.io/mongo-csharp-driver/2.7/getting_started/quick_tour/ 4 | 5 | Here are some examples (in C#) for Inserting/querying/updating/deleting documents on MongoDb. 6 | 7 | - https://docs.mongodb.com/manual/tutorial/insert-documents/ 8 | - https://docs.mongodb.com/manual/tutorial/query-documents/ 9 | - https://docs.mongodb.com/manual/tutorial/update-documents/ 10 | - https://docs.mongodb.com/manual/tutorial/remove-documents/ 11 | 12 | Please note that the examples use plain `BsonDocuments`. `BsonDocument` could be substituted with POCO (plain old c# object) which would make the API easier to use. 13 | 14 | In practice this means converting a line like this: 15 | `var filter = Builders.Filter.Eq("name", "John");` 16 | into this: 17 | `var filter = Builders.Filter.Eq("name", "John");` 18 | which would make a filter that can be used for queries which return `Player` objects instead of `BsonDocument` objects. 19 | 20 | - Aggregation pipeline: https://docs.mongodb.com/manual/core/aggregation-pipeline/ 21 | -------------------------------------------------------------------------------- /resources/aspnet-middleware-conf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/aspnet-middleware-conf.png -------------------------------------------------------------------------------- /resources/aspnet-middleware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/aspnet-middleware.png -------------------------------------------------------------------------------- /resources/aspnet-simple-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/aspnet-simple-pipeline.png -------------------------------------------------------------------------------- /resources/claims.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/claims.png -------------------------------------------------------------------------------- /resources/filter-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/filter-pipeline.png -------------------------------------------------------------------------------- /resources/fps-server-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/fps-server-architecture.png -------------------------------------------------------------------------------- /resources/http-messaging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/http-messaging.png -------------------------------------------------------------------------------- /resources/nosql-app-integration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-app-integration.png -------------------------------------------------------------------------------- /resources/nosql-cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-cap.png -------------------------------------------------------------------------------- /resources/nosql-data-distribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-data-distribution.png -------------------------------------------------------------------------------- /resources/nosql-data-models.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-data-models.png -------------------------------------------------------------------------------- /resources/nosql-hardware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-hardware.png -------------------------------------------------------------------------------- /resources/nosql-history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-history.png -------------------------------------------------------------------------------- /resources/nosql-replication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-replication.png -------------------------------------------------------------------------------- /resources/nosql-sharding.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/nosql-sharding.png -------------------------------------------------------------------------------- /resources/simple-server-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/simple-server-architecture.png -------------------------------------------------------------------------------- /resources/social-game-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/social-game-architecture.png -------------------------------------------------------------------------------- /resources/web-api-beyond-controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vsillan/game-server-programming-course/4b9495de11adef2cc7c9dc74ba1d65a31f38aa4a/resources/web-api-beyond-controller.png --------------------------------------------------------------------------------