<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Myles Broomes</title>
    <description>The latest articles on Forem by Myles Broomes (@mylesb93).</description>
    <link>https://forem.com/mylesb93</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F193597%2F9cdbfa9f-0cf4-4e97-bae8-c66a658d238b.jpg</url>
      <title>Forem: Myles Broomes</title>
      <link>https://forem.com/mylesb93</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/mylesb93"/>
    <language>en</language>
    <item>
      <title>Personal Project: Backlog Tracker</title>
      <dc:creator>Myles Broomes</dc:creator>
      <pubDate>Mon, 01 Jul 2024 14:45:16 +0000</pubDate>
      <link>https://forem.com/mylesb93/personal-project-backlog-tracker-566i</link>
      <guid>https://forem.com/mylesb93/personal-project-backlog-tracker-566i</guid>
      <description>&lt;p&gt;As someone who doesn't build nearly enough of my own projects, I thought I would try my hand at building something fun, using as many of the skills that I know as I could. I wanted to build something that I would actually use, and as someone who plays a lot of video games, I thought it'd be cool to create a web application that is able to allow users to track which games they're currently playing, which games they plan to play and which games they've completed. A "Backlog Tracker", if you will.&lt;/p&gt;


&lt;div&gt;
  &lt;iframe src="https://loom.com/embed/a4b0871bb0f64f78b207a1140cba5c5e"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Project Management
&lt;/h2&gt;

&lt;p&gt;The first step was to decide upon on the MVP (or Minimum Viable Project). The three most important features I decided that were needed were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User's should be able to register an account&lt;/li&gt;
&lt;li&gt;User's should be able to search against a large database of games&lt;/li&gt;
&lt;li&gt;User's should be able to add and remove games to and from their backlog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step was then to decide what I would use to manage project tasks. I went with Trello as it would allow me to create cards for each individual task. I can also add labels and descriptions to each card to make it easier to organize them:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwuhnekshc3t66qx989u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flwuhnekshc3t66qx989u.gif" alt="Trello Demo" width="720" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Building The Project
&lt;/h2&gt;

&lt;p&gt;I made the decision to build the project using .NET as that's what I'm most comfortable using, as I use it in my day job.&lt;/p&gt;

&lt;p&gt;It was then finally time to get my hands dirty with some coding. The frontend of the application is built using &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-8.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;.NET Razor Pages&lt;/a&gt;. Razor Pages are split into a razor page containing the razor markup, and a C# class (known as the page model) where any necessary logic is included. For example, in this project the homepage is a simple page that, initially, just contains a search bar. Here is how the razor markup for that page looks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@page
@using Microsoft.AspNetCore.Html
@model IndexModel
@{
    ViewData["Title"] = "Home page";
    var user = Model.User.Identity;
}

&amp;lt;div class="text-center"&amp;gt;
    &amp;lt;h1 class="display-4"&amp;gt;Backlog Tracker&amp;lt;/h1&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;div id="errorMessage"&amp;gt;&amp;lt;/div&amp;gt;

&amp;lt;form method="post" style="margin-bottom:1rem"&amp;gt;
    &amp;lt;div class="input-group"&amp;gt;
        &amp;lt;input class="form-control" asp-for="SearchTerm" name="query"&amp;gt;
        &amp;lt;div class="input-group-append"&amp;gt;
            &amp;lt;button class="btn btn-primary" type="submit"&amp;gt;Search&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/form&amp;gt;

@if (Model.GamesResponse?.Games is { Count: &amp;gt; 0 })
{
    &amp;lt;div class="row row-cols-4"&amp;gt;
        @foreach (var game in Model.GamesResponse.Games)
        {
            &amp;lt;div class="col"&amp;gt;
                &amp;lt;div class="card game-card"&amp;gt;
                    &amp;lt;div class="card-body"&amp;gt;
                        &amp;lt;h5 class="card-title"&amp;gt;
                            &amp;lt;a href="@game.SiteDetailUrl" target="_blank"&amp;gt;@game.Name&amp;lt;/a&amp;gt;
                        &amp;lt;/h5&amp;gt;
                        @if (!string.IsNullOrEmpty(game.Description))
                        {
                            &amp;lt;div class="card-text-container"&amp;gt;
                                &amp;lt;p class="card-text"&amp;gt;@(new HtmlString(game.Description))&amp;lt;/p&amp;gt;
                            &amp;lt;/div&amp;gt;
                        }
                        @if (user !=null &amp;amp;&amp;amp; user.IsAuthenticated)
                        {
                            &amp;lt;a class="btn btn-primary" id="btn_@game.Id" data-game-id="@game.Id" data-game-name="@game.Name" onclick="addToBacklog(this,'@user?.Name?.Trim()')"&amp;gt;+&amp;lt;/a&amp;gt;
                        }
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        }
    &amp;lt;/div&amp;gt;
}
else if (Model.GamesResponse?.Games is { Count: 0 })
{
    &amp;lt;p&amp;gt;@ViewData["NoResults"]&amp;lt;/p&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Model&lt;/code&gt; object is the page model which is processed in a separate file. Entering text into the search bar and hitting enter, makes a post request (handled by the &lt;code&gt;OnPost()&lt;/code&gt; method in the C# page model), which makes an API call to the &lt;a href="https://www.giantbomb.com/api/documentation/" rel="noopener noreferrer"&gt;Giant Bomb API&lt;/a&gt; (more on that later) and then displays relevant data on the page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public IndexModel(IGameService gameService, ILogger&amp;lt;IndexModel&amp;gt; logger)
{
    _gameService = gameService;
    _logger = logger;
}

public async void OnPost()
{            
    string? query = Request.Form["query"];

    if (!string.IsNullOrEmpty(query))
    { 
        try
        {
            _logger.LogInformation("Fetching games from GiantBomb API...");
            GamesResponse = await _gameService.GetGamesAsync(Request.Form["query"]);
            ViewData["NoResults"] = GamesResponse == null ? "" : "No Games Matching The Query!";
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error occurred while fetching games - {ex.Message}");
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All logic that involves interacting directly with the Giant Bomb API is contained in the &lt;a href="https://github.com/MylesB93/backlog-tracker/blob/master/BacklogTracker/Services/GiantBombService.cs" rel="noopener noreferrer"&gt;GiantBombService&lt;/a&gt;. As you can see, I inject the service into the constructor (using &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection" rel="noopener noreferrer"&gt;dependency injection&lt;/a&gt;) of the page model and then use it to call &lt;code&gt;GetGamesAsync&lt;/code&gt;. The data from this API call is stored in the &lt;code&gt;GamesReponse&lt;/code&gt; property (which is an object of type &lt;a href="https://github.com/MylesB93/backlog-tracker/blob/master/BacklogTracker/Models/Response.cs" rel="noopener noreferrer"&gt;Response&lt;/a&gt;) on the page model class, which can then be used on the frontend.&lt;/p&gt;

&lt;p&gt;I needed an API which would allow me to pull from a database containing information about video games so, after a bit of research, I came across the Giant Bomb API which does exactly what I would need. The user would search for a game within my application, which would request data about said game from the API then the application can handle the returned data as needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public async Task&amp;lt;Response&amp;gt; GetGamesAsync(string? query)
{
    var games = new Response();

    var client = _httpClientFactory.CreateClient("GiantBomb");

    var response = client.GetAsync($"/api/search/?api_key={_giantBombConfiguration.Value.GiantBombAPIKey}&amp;amp;query={query}&amp;amp;resources=game&amp;amp;field_list=name,site_detail_url,description,id").Result;

    XmlSerializer xs = new XmlSerializer(typeof(Response));

    using (StreamReader reader = new StreamReader(await response.Content.ReadAsStreamAsync()))
    {
        try
        {
            games = (Response?)xs.Deserialize(reader);
            _logger.LogInformation("Deserialization complete!");
        }
        catch (Exception ex)
        {
            _logger.LogError($"Error occurred during deserialization: {ex.Message}");
        }
    }

    _logger.LogInformation("Request complete!");

    return games ?? new Response();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the data we receive from the API is in XML format so we need to deserialize that before being able to use it.&lt;/p&gt;

&lt;p&gt;I needed a way of persisting user data so I decided to use &lt;a href="https://learn.microsoft.com/en-us/ef/core/" rel="noopener noreferrer"&gt;Entity Framework&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-8.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;.NET Identity&lt;/a&gt;. This took out a lot of the complexity around doing things like setting up my own SQL database and building my own user authentication functionality.&lt;/p&gt;

&lt;p&gt;The user would need to be able to add game data to their backlog - once signed in, they'd have the ability to click a button next to each game which would add it to their backlog. I decided to create my own .NET Web API to handle this. There is a JavaScript event listener on each button which would call my API and handle updating that user's backlog in the database. Here is an example of how that process works for the "AddToBacklog()" method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@if (Model.GamesResponse?.Games is { Count: &amp;gt; 0 })
{
    &amp;lt;div class="row row-cols-4"&amp;gt;
        @foreach (var game in Model.GamesResponse.Games)
        {
            &amp;lt;div class="col"&amp;gt;
                &amp;lt;div class="card game-card"&amp;gt;
                    &amp;lt;div class="card-body"&amp;gt;
                        &amp;lt;h5 class="card-title"&amp;gt;
                            &amp;lt;a href="@game.SiteDetailUrl" target="_blank"&amp;gt;@game.Name&amp;lt;/a&amp;gt;
                        &amp;lt;/h5&amp;gt;
                        @if (!string.IsNullOrEmpty(game.Description))
                        {
                            &amp;lt;div class="card-text-container"&amp;gt;
                                &amp;lt;p class="card-text"&amp;gt;@(new HtmlString(game.Description))&amp;lt;/p&amp;gt;
                            &amp;lt;/div&amp;gt;
                        }
                        @if (user !=null &amp;amp;&amp;amp; user.IsAuthenticated)
                        {
                            &amp;lt;a class="btn btn-primary" id="btn_@game.Id" data-game-id="@game.Id" data-game-name="@game.Name" onclick="addToBacklog(this,'@user?.Name?.Trim()')"&amp;gt;+&amp;lt;/a&amp;gt;
                        }
                    &amp;lt;/div&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;
        }
    &amp;lt;/div&amp;gt;
}
else if (Model.GamesResponse?.Games is { Count: 0 })
{
    &amp;lt;p&amp;gt;@ViewData["NoResults"]&amp;lt;/p&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;for&lt;/code&gt; loop adds &lt;code&gt;onclick="addToBacklog(this,'@user?.Name?.Trim()')&lt;/code&gt; to each button, which calls the following function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;﻿function addToBacklog(element, email) {
    const apiUrl = '/api/backlog/add-game-to-backlog';

    const requestData = {
        Email: email,
        GameID: element.getAttribute("data-game-id")
    };

    fetch(apiUrl, {
        method: 'PATCH',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(requestData)
    })
        .then(response =&amp;gt; {
            if (response.ok)
                return response.json();
            else if (response.status == 409)
                throw new Error("Game already exists in backlog!");
            else
                throw new Error('Network response was not ok.')
        })
        .then(data =&amp;gt; {
            alert(element.getAttribute("data-game-name") + " successfully added to backlog!");
        })
        .catch(error =&amp;gt; {
            var errorMsg = document.getElementById("errorMessage");
            console.error('There was a problem sending data to the API:', error);
            errorMsg.innerHTML = error;
            errorMsg.style.visibility = 'visible';
        });

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;/api/backlog/add-game-to-backlog&lt;/code&gt; is the location of my Web API which calls the &lt;code&gt;AddToBacklog()&lt;/code&gt; method. It gets passed a &lt;code&gt;UserDto&lt;/code&gt; object which is an object containing the email address of the current user and the ID of the game being added to the backlog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[HttpPatch("add-game-to-backlog")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult AddToBacklog([FromBody] UserDto userDto)
{
    try
    {
        _backlogService.AddToBacklog(userDto);
        return Ok(new { Message = "Data saved successfully." });
    }
    catch (ArgumentException ex)
    {
        return Conflict(ex.Message);
    }
    catch (Exception ex)
    {
        return StatusCode(500, $"An error occurred while processing the request - {ex.Message}.");
    }            
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://github.com/MylesB93/backlog-tracker/blob/master/BacklogTracker/Services/BacklogService.cs" rel="noopener noreferrer"&gt;BacklogService&lt;/a&gt; in this example is injected into this classes constructor (just like with the GiantBombService) and then can be used to handle interacting directly with the database. The &lt;code&gt;AddToBacklog()&lt;/code&gt; method above is able to consume data from the request body of the API call and then send it off to the backlog service to be added to the database. A similar process is used when user's move games from their backlog to their completed games list. They click a button that has a JavaScript event listener, which makes a request to my API, which then moves the game from the backlog column in the database to the completed games column. &lt;/p&gt;

&lt;h2&gt;
  
  
  Unit Testing with xUnit
&lt;/h2&gt;

&lt;p&gt;I wanted a way of incorporating unit testing into the project, so I decided to use xUnit and I have created a separate project which contains the test suite. Within these tests, I've mocked the backlog service which allowed me to just focus on the functionality being tested (the API controller in this case). For this, I used the &lt;a href="https://www.nuget.org/packages/Moq/" rel="noopener noreferrer"&gt;Moq&lt;/a&gt; package and created the following object:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_backlogServiceMock = new Mock&amp;lt;IBacklogService&amp;gt;();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This then allowed me to mock each method from the service and how that method should behave when called in my test. For example, one of the things I wanted to test was, when the &lt;code&gt;GetUsersBacklog()&lt;/code&gt; method is called in the API, it should return a valid list of game ID's when passed a specific email address:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_backlogServiceMock.Setup(s =&amp;gt; s.GetBacklog("mylesbroomestest@hotmail.co.uk")).Returns(new List&amp;lt;string&amp;gt;() { "1234", "4567", "7890" });&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This basically translates to when the string "&lt;a href="mailto:mylesbroomestest@hotmail.co.uk"&gt;mylesbroomestest@hotmail.co.uk&lt;/a&gt;" is passed to the &lt;code&gt;GetBacklog()&lt;/code&gt; method, return a list of strings containing "1234", "4567" and "7890". Here's how it looks in action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Fact]
public void GetUsersBacklog_WhenCalled_ReturnsListOfGameIds()
{
    // Act
    var result = _controller.GetUsersBacklog("mylesbroomestest@hotmail.co.uk") as OkObjectResult;

    // Assert
    var gameIds = Assert.IsType&amp;lt;List&amp;lt;string&amp;gt;&amp;gt;(result?.Value);
    Assert.Equal(3, gameIds.Count);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u09h72chee14a3ytw93.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u09h72chee14a3ytw93.png" alt="The gameIDs list" width="800" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Overall, this was an extremely fun project to build. It required me to learn several things I didn't know already and I feel like I've really expanded my knowledge of .NET, working with API's, dependency injection and unit testing from working on it.&lt;/p&gt;

&lt;p&gt;Here is the repository of the completed project if you want to see the code for yourself: &lt;a href="https://github.com/MylesB93/backlog-tracker/tree/master" rel="noopener noreferrer"&gt;https://github.com/MylesB93/backlog-tracker/tree/master&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Thanks for reading and happy coding! 👨🏾‍💻&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>dotnetcore</category>
      <category>backend</category>
    </item>
    <item>
      <title>Entity Framework Core in .NET 7 7️⃣</title>
      <dc:creator>Myles Broomes</dc:creator>
      <pubDate>Mon, 26 Jun 2023 18:32:32 +0000</pubDate>
      <link>https://forem.com/mylesb93/entity-framework-core-in-net-7-7-254f</link>
      <guid>https://forem.com/mylesb93/entity-framework-core-in-net-7-7-254f</guid>
      <description>&lt;p&gt;I wanted to show a simple example of how Entity Framework Core works in .NET (specifically .NET 7 in this example). Entity Framework is what's known as an "Object-relational mapper" (or ORM for short) which allows developers to create classes that can be mapped to a database. &lt;/p&gt;

&lt;p&gt;I've built a directory containing information about soccer players, teams, leagues and stadiums. The project (which can be found &lt;a href="https://github.com/MylesB93/football-directory" rel="noopener noreferrer"&gt;here&lt;/a&gt;) is a .NET core project and uses Razor Pages for the front-end views.&lt;/p&gt;

&lt;p&gt;Below you can see a list of all of the players that have been added to the directory, and if I select "details" for one of the players, I can view further information (namely, which team they play for):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnw9sk28ydtwxbdoaunh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnw9sk28ydtwxbdoaunh.png" alt="A preview of how players are listed within the application" width="800" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1narb0f6nxkycyvcsc0x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1narb0f6nxkycyvcsc0x.png" alt="A preview of how player information can be seen within the application" width="393" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see how the players' &lt;code&gt;Team Name&lt;/code&gt; matches against the name of the team in the application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo759w6txupw6jzvagtb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpo759w6txupw6jzvagtb.png" alt="A preview of how team information can be seen within the application" width="742" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my database, I want separate tables for &lt;code&gt;Teams&lt;/code&gt;, &lt;code&gt;Players&lt;/code&gt;, &lt;code&gt;Leagues&lt;/code&gt; and &lt;code&gt;Stadiums&lt;/code&gt; so the first step is to create classes for each of those:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class Team
    {
        public int TeamID { get; set; }
        public string Name { get; set; }
        public int LeagueID { get; set; }
        public League League { get; set; }
        public int StadiumID { get; set; }
        public ICollection&amp;lt;Player&amp;gt; Players { get; set;}
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class Player
    {
        public int PlayerID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int TeamID { get; set; }
        public PlayerPosition Position { get; set; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class League
    {
        public int LeagueID { get; set; }
        public ICollection&amp;lt;Team&amp;gt; Teams { get; set; }
        public int NumberOfTeams { get; set; }
        public string Name { get; set; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class Stadium
    {
        public int StadiumID { get; set; }
        public int TeamID { get; set; }
        public int Capacity { get; set; }
        public string Name { get; set; }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relationships of those tables is as followed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;One &lt;code&gt;Team&lt;/code&gt; can have many &lt;code&gt;Players&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One &lt;code&gt;Team&lt;/code&gt; has one &lt;code&gt;Stadium&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;One &lt;code&gt;League&lt;/code&gt; has many &lt;code&gt;Stadiums&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each class has an ID property (which will represent the primary key in the database). Some classes also have an ID of another type (e.g. the &lt;code&gt;Stadium&lt;/code&gt; class has an ID for &lt;code&gt;TeamID&lt;/code&gt;). This represents a foreign key in the database. Also, you'll notice how the &lt;code&gt;Team&lt;/code&gt; class contains an &lt;code&gt;ICollection&lt;/code&gt; of type &lt;code&gt;Player&lt;/code&gt;, and the &lt;code&gt;League&lt;/code&gt; class contains an &lt;code&gt;ICollection&lt;/code&gt; of type &lt;code&gt;Team&lt;/code&gt;. When we create a class with an ICollection, the table will be represented as a one-to-many relationship in the database.&lt;/p&gt;

&lt;p&gt;I've also created a separate class containing seed data, so we will have initial data in the database after running the application for the first time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public static class DbInitializer
    {
        public static void Initialize(TeamContext context)
        {
            if (context.Teams.Any())
            {
                return;
            }

            var leagues = new League[]
            {
                new League{Name="Ligue 1",NumberOfTeams=20},
                new League{Name="English Premier League",NumberOfTeams=20},
                new League{Name="La Liga",NumberOfTeams=20},
                new League{Name="Serie A",NumberOfTeams=20},
                new League{Name="Bundesliga",NumberOfTeams=18}
            };

            context.Leagues.AddRange(leagues);
            context.SaveChanges();

            var teams = new Team[]
            {
                new Team{Name="Paris Saint Germain",LeagueID=1,StadiumID=1},
                new Team{Name="Tottenham Hotspur",LeagueID=2,StadiumID=2},
                new Team{Name="Real Madrid",LeagueID=3,StadiumID=3},
                new Team{Name="AC Milan",LeagueID=4,StadiumID=4},
                new Team{Name="Bayern Munich",LeagueID=5,StadiumID=5},
                new Team{Name="Chelsea",LeagueID=2,StadiumID=6},
                new Team{Name="Internazionale",LeagueID=4,StadiumID=4}
            };

            context.Teams.AddRange(teams);
            context.SaveChanges();

            var players = new Player[]
            {
                new Player{FirstName="Lionel",LastName="Messi",TeamID=1,Position=PlayerPosition.Midfielder},
                new Player{FirstName="Harry",LastName="Kane",TeamID=2,Position=PlayerPosition.Forward},
                new Player{FirstName="Vinicius",LastName="Jr",TeamID=3,Position=PlayerPosition.Forward},
                new Player{FirstName="Rafael",LastName="Leao",TeamID=4,Position=PlayerPosition.Forward},
                new Player{FirstName="Joshua",LastName="Kimmich",TeamID=5,Position=PlayerPosition.Defender},
                new Player{FirstName="Enzo",LastName="Fernandez",TeamID=6,Position=PlayerPosition.Midfielder},
                new Player{FirstName="Lautaro",LastName="Martinez",TeamID=7,Position=PlayerPosition.Forward}
            };

            context.Players.AddRange(players);
            context.SaveChanges();                      

            var stadiums = new Stadium[]
            {
                new Stadium{Name="Parc de Princes",TeamID=1,Capacity=50000},
                new Stadium{Name="Tottenham Hotspur Stadium",TeamID=2,Capacity=60000},
                new Stadium{Name="Santiago Bernabeu Stadium",TeamID=3,Capacity=80000},
                new Stadium{Name="San Siro",TeamID=4,Capacity=75000},
                new Stadium{Name="Aliianz Arena",TeamID=5,Capacity=75000},
                new Stadium{Name="Stamford Bridge",TeamID=6,Capacity=40000}
            };

            context.Stadiums.AddRange(stadiums);
            context.SaveChanges();
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the &lt;code&gt;Initialize()&lt;/code&gt; method is run, you can see it first checks &lt;code&gt;context.Teams&lt;/code&gt; is populated, and if it's not (i.e. if this is the first time we're running the application) it creates several objects and saves them to the database. The &lt;code&gt;TeamContext&lt;/code&gt; class is a class that inherits from Entity Framework's &lt;code&gt;DbContext&lt;/code&gt; class and is used to define how we want our database to be setup. You can set which tables (&lt;code&gt;DbSet&lt;/code&gt;) should be created from which classes and anytime we want to update our database we can access this object (e.g. in our &lt;code&gt;DbInitializer&lt;/code&gt; above we're adding to a list then calling &lt;code&gt;context.SaveChanges()&lt;/code&gt; to save those changes to the database.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    public class TeamContext : DbContext
    {
        public TeamContext (DbContextOptions&amp;lt;TeamContext&amp;gt; options)
            : base(options)
        {
        }

        public DbSet&amp;lt;Team&amp;gt; Teams { get; set; }
        public DbSet&amp;lt;Player&amp;gt; Players { get; set; }
        public DbSet&amp;lt;League&amp;gt; Leagues { get; set; }
        public DbSet&amp;lt;Stadium&amp;gt; Stadiums { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity&amp;lt;Team&amp;gt;().ToTable("Team");
            modelBuilder.Entity&amp;lt;Player&amp;gt;().ToTable("Player");
            modelBuilder.Entity&amp;lt;League&amp;gt;().ToTable("League");
            modelBuilder.Entity&amp;lt;Stadium&amp;gt;().ToTable("Stadium");
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As mentioned before, we're using &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/razor-pages/?view=aspnetcore-7.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;Razor Pages&lt;/a&gt; for the views. .NET makes creating the views for Create, Read, Update and Delete of entities really easy. I initially created razor views for the player entities in Visual Studio like so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Right-click on the folder where you want your views to be&lt;/li&gt;
&lt;li&gt;Hover over "Add" and select "Razor Page"&lt;/li&gt;
&lt;li&gt;Select "Razor Pages using Entity Framework (CRUD)" and hit "Add"&lt;/li&gt;
&lt;li&gt;In the dialogue that appears, set "Model Class" to whichever class you want to create razor pages for (&lt;code&gt;Player&lt;/code&gt; in my case)&lt;/li&gt;
&lt;li&gt;Select your context class as the "DbContext class" and hit "Add"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Visual Studio will then create some razor pages for you:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyg2lkpjuklh72czod5vz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyg2lkpjuklh72czod5vz.png" alt="Showing how Razor Pages look within the Visual Studio solution" width="389" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you run your application, you'll be able to see all of the rows from your database:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database&lt;/strong&gt;:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic1i1eqa67a0vzv5ds6w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fic1i1eqa67a0vzv5ds6w.png" alt="A preview of how the database tables look once created by Entity Framework" width="542" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwii5ktno2ktdjdkl4e3s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwii5ktno2ktdjdkl4e3s.png" alt="A preview of how the database tables look when presented in the application" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I only wanted to give a simple preview of what can be done with Entity Framework, but if this is something that interests you and you want to go further in-depth with all the possibilities, I recommend checking out the &lt;a href="https://learn.microsoft.com/en-us/ef/" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; where you can also find a great &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/intro?view=aspnetcore-7.0&amp;amp;tabs=visual-studio" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt; which will guide you through building your very own .NET Core web application.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>database</category>
      <category>oop</category>
    </item>
    <item>
      <title>Security headers - what they are and how to use them 🔒</title>
      <dc:creator>Myles Broomes</dc:creator>
      <pubDate>Tue, 06 Jun 2023 14:26:25 +0000</pubDate>
      <link>https://forem.com/mylesb93/security-headers-what-they-are-and-how-to-use-them-2nmg</link>
      <guid>https://forem.com/mylesb93/security-headers-what-they-are-and-how-to-use-them-2nmg</guid>
      <description>&lt;p&gt;I was recently tasked with improving the security rating on one of our websites. This involved a couple of things but the thing I want to focus on in this post is security headers. We scanned the site &lt;a href="https://securityheaders.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt; and were initially given a rating of 'E'. Not good. So one of the recommendations was to add security headers which are headers contained in the HTTP response and can provide various different security benefits, such as only allowing iFrame's from specified sites to be embedded on your site or ensuring your browser is only accessed via HTTPS. By adding some of these headers, we were able to raise the security rating to an 'A'.&lt;/p&gt;

&lt;p&gt;There are various ways you can add these headers depending on your server setup but the most straightforward is adding them in a &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Apache_Configuration_htaccess" rel="noopener noreferrer"&gt;".htaccess"&lt;/a&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Security Headers
&amp;lt;IfModule mod_headers.c&amp;gt;
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains"
&amp;lt;/IfModule&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we're adding the "Strict-Transport-Security" header which, as mentioned previously, ensures your site can only be accessed via HTTPS.&lt;/p&gt;

&lt;p&gt;Here are some specific examples:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;X-Content-Type-Options&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
Prevents the browser from doing what's known as MIME-type sniffing, which is when the browser tries to figure out the content type by looking at the content of a resource, rather than looking at the Content-Type header. &lt;code&gt;nosniff&lt;/code&gt; is the only possible value that this header can be set to. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;X-Frame-Options&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
Specifies whether or not your site can be embedded inside an iFrame on other sites. Examples of possible values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DENY&lt;/code&gt; -&amp;gt; Ensures the site can't be displayed in an iFrame&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SAMEORIGIN&lt;/code&gt; -&amp;gt; The site can be displayed in an iFrame if all ancestor frames are from the same origin of the site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Referrer-Policy&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
Specifies how much information should be contained in the &lt;code&gt;Referrer&lt;/code&gt; header (e.g. do we want the entire path of the referrer to be shown?). Examples of possible values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;origin&lt;/code&gt; -&amp;gt; Only the origin is shown in the &lt;code&gt;Referrer&lt;/code&gt; header (e.g. if the referrer is &lt;a href="https://example.com/test-page" rel="noopener noreferrer"&gt;https://example.com/test-page&lt;/a&gt; then only &lt;a href="https://example.com" rel="noopener noreferrer"&gt;https://example.com&lt;/a&gt; is shown in the &lt;code&gt;Referrer&lt;/code&gt; header)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;same-origin&lt;/code&gt; -&amp;gt; Only show the referrer for same-origin requests (when we're being referred from the same site). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Strict-Transport-Security&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;
Ensures your website can only be served over HTTPS. Examples of possible values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;max-age=&amp;lt;expire-time&amp;gt;&lt;/code&gt; -&amp;gt; How long in seconds that the browser remembers to serve the site over HTTPS&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;includeSubDomains&lt;/code&gt; -&amp;gt; Whether this header should also apply to subdomains (e.g. &lt;a href="https://subdomain.example.com" rel="noopener noreferrer"&gt;https://subdomain.example.com&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;Content-Security-Policy&lt;/u&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A list of rules (known as a policy) that apply to specified domains to protect against certain types of attacks (e.g. &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Cross-site_scripting" rel="noopener noreferrer"&gt;Cross-Site Scripting&lt;/a&gt;). For example, you can set &lt;code&gt;script-src https://www.example.com&lt;/code&gt; which will ensure that only scripts from &lt;a href="https://www.example.com" rel="noopener noreferrer"&gt;https://www.example.com&lt;/a&gt; can be run on your site. There's a lot you can do here which you can find out about here: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" rel="noopener noreferrer"&gt;https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've done everything correctly then you should be able to see your header by viewing the network tab in your browser of choice:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47d871st2jfqhshym7hn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F47d871st2jfqhshym7hn.png" alt=" " width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hopefully you found that useful and if so, feel free to drop a comment below and find me on Twitter - &lt;a href="https://twitter.com/MylesB93" rel="noopener noreferrer"&gt;https://twitter.com/MylesB93&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>http</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
