GA Logo

Opinionated Framework - Models & Migrations


What you will learn

  • How to set the database settings
  • how create models and migrations
  • how to use models to add, update, and delete records

Setup

  • Open up the same project from this morning
  • download the following libraries with nuget (.net package manager)
dotnet add package EntityFramework --version 6.4.4
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 5.0.2
dotnet add package Microsoft.EntityFrameworkCore.Design --version 5.0.4

Entity is the .Net frameworks ORM for working with Databases.

Setting Up the Database Settings

  • create a .gitignore file with the following
/obj
/out
appsettings.json
  • create a postgres database with command createdb firstdotnetdb
  • add the following to appsettings.json reflecting your local postgres username and password.
  "DBContextSettings": {
    "ConnectionString": "User ID=test5;Password=test5;Host=localhost;Port=5432;Database=firstdotnetdb;Pooling=true;"
  }

Creating Our Model

Create a Models folder and create a file called Turtle.cs.

namespace firstdotnet.Models
{
    public class Turtle
    {
        public long id {get; set;}
        public string name {get; set;}
        public int age {get; set;}
    }
}

Pretty straight forward, the properties of the class will be used later in determining how to execute migrations.

Now we need to create a Database Context for our model, this is the class the connects the model to the database. Create a new file TurtleContext.cs in the Models folder.

using Microsoft.EntityFrameworkCore;

namespace firstdotnet.Models
{
    public class TurtleContext : DbContext
    {
        public TurtleContext(DbContextOptions<TurtleContext> options) : base(options)
        {
        }
        // Database Contexts can had db sets for multiple models, you add them as properties, you can also create mutliple database contexts
        public DbSet<Turtle> Turtles { get; set; } 
    }
}

Now that we have our database context we need to register it as a service so our controllers can receive it through dependency injection. In Startup.cs we will edit out configuration function.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.OpenApi.Models;
using firstdotnet.Models;
using Microsoft.EntityFrameworkCore;

namespace firstdotnet
{
    public class Startup
    {
        // Constructor: Grabbing the configurtation object that will let us grab variables from appsettings.
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // the property to receive the configuration object
        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {

            // The collects all our controllers for routing
            services.AddControllers();

            // We save the connection string appsettings.json in a variable
            var connectionString = Configuration["DbContextSettings:ConnectionString"];
            // We register the db context as a service
            services.AddDbContext<TurtleContext>(opt => opt.UseNpgsql(connectionString));

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "firstdotnet", Version = "v1" });
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "firstdotnet v1"));
            }
            
            // forces site to redirect to https, comment out for now
            // app.UseHttpsRedirection();

            app.UseRouting(); // enables writing

            app.UseAuthorization(); // enables authorization

            // this function is where we define all our routing
            app.UseEndpoints(endpoints =>
            {
                // Enable Attribute Routing
                endpoints.MapControllers();
                // Enable Pattern Matching
                endpoints.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{URLParam?}");

            });
        }
    }
}

Migrations

We should be all wired up! Now let's have the Entity Framework migrate our database!

If you haven't yet, you need to download the Entity Framework tools

dotnet tool install --global dotnet-ef
  • Once that is installed let's create our initial migration
  • dotnet ef migrations add InitialCreate
  • then run the migrations with dotnet ef database update

We're good to go, now we just need to build out controller!

Using the Model

We will use attribute routing. Create a new controller, TurtleController.cs, in the controllers folder.

using firstdotnet.Models;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc; // For the Route and Http attributes
using System.Linq; // For the IEnumerable interface

namespace firstdotnet.Controllers
{
    [Route("turtles")] //The Controller handles routes for /turtles
    public class TurtleController
    {
        // declare property to hold Database Context
        private readonly TurtleContext turtles;

        // define constructor to receive database context via DI
        public TurtleController(TurtleContext turtlesCtx){
            turtles = turtlesCtx;
        }

        [HttpGet] // get request to "/turtles"
        public IEnumerable<Turtle> index(){
            // return all the turtles
            return turtles.Turtles.ToList();
        }

        [HttpPost] // post request to "/turtles"
        public IEnumerable<Turtle> Post([FromBody]Turtle Turtle){
            // add a Turtle
            turtles.Turtles.Add(Turtle);
            // save changes
            turtles.SaveChanges();
            // return all the turtles
            return turtles.Turtles.ToList();
        }

        [HttpGet("{id}")] // get requestion to "turtles/{id}"
        public Turtle show(long id){
            // return the specified Turtle matched based on id
            return turtles.Turtles.FirstOrDefault(x => x.id == id);
        }

        [HttpPut("{id}")] // put request to "turtles/{id}
        public IEnumerable<Turtle> update([FromBody]Turtle Turtle, long id){
            // retrieve Turtle to be updated
            Turtle oldTurtle = turtles.Turtles.FirstOrDefault(x => x.id == id);
            //update their properties, can also be done with turtles.Turtles.Update
            oldTurtle.name = Turtle.name;
            oldTurtle.age = Turtle.age;
            // Save changes
            turtles.SaveChanges();
            // return updated list of turtles
            return turtles.Turtles.ToList();
        }

        [HttpDelete("{id}")] // delete request to "turtles/{id}
        public IEnumerable<Turtle> destroy(long id){
            //retrieve existing Turtle
            Turtle oldTurtle = turtles.Turtles.FirstOrDefault(x => x.id == id);
            //remove them
            turtles.Turtles.Remove(oldTurtle);
            // saves changes
            turtles.SaveChanges();
            // return updated list of turtles
            return turtles.Turtles.ToList();
        }
    }
}

Test all the routes! You've done it, head to lab!


Resources to Learn More