.Net 5 Web API With Repository Pattern Docker and PostgreSQL

Before we start coding make sure your environment is ready. Make sure you have .net 5 sdk install in your machine. If not download it form this link https://dotnet.microsoft.com/download/dotnet/5.0

We will also use docker in this tutorial, so make sure to install it. You can get docker from this link https://www.docker.com/products/docker-desktop

We will be using Visual Studio Code in this tutorial, so make sure you have it in your machine. Here is the link to download visual studio code https://code.visualstudio.com/download

Advertisements

We also need some additional extensions for our Visual Studio Code. Open Visual Studio Code then click the icon for extensions

Install this three extensions
  • C#
  • C# Extensions
  • NuGet Package Manager

If you are done with your setup, it’s time to create our project.

In the menu, click Terminal then New Terminal to open a terminal in Visual Studio Code.

It should open a new terminal at the bottom of the window. Just like this

In the terminal, navigate to directory where you want to put your project. For me, I want to put it in my projects folder in my documents.

cd documents/projects
Advertisements

Then create a new web api project name ProductsAPI, by executing this command

dotnet new webapi -n ProductsApi

Then open the folder where you put your new project in visual studio code. After opening it for the first time, you will got this message at the lower right side of visual studio code. Just click Yes.

Then let’s test if everything is working. Click the Run and Debug

Then click this play button

It should open the app in your browser. Navigate to this URL https://localhost:5001/swagger/index.html If everything works well you should have this

but if ever you got this screen

Go back to visual studio code again and run this command in the terminal
dotnet dev-certs https --trust

Then a window will appear. Just click the Yes button.

Advertisements

Now we know our Web Api is working, we can go back to Visual Studio and start coding. Before making any change, make sure you stop the project by clicking in the square button.

Add new file called docker-compose.yml

And paste this inside of it.
version: '3.4'

services:

  postgresql_database:
    image: postgres:latest
    environment:
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=admin1234
      - POSTGRES_DB=productsDb
    ports:
      - "5432:5432"
    restart: always
    volumes:
      - database-data:/var/lib/postgresql/data/
    
  pgadmin:
    image: dpage/pgadmin4
    environment:
      - PGADMIN_DEFAULT_EMAIL=pgadmin4@pgadmin.org
      - PGADMIN_DEFAULT_PASSWORD=admin1234
    ports:
      - "5050:80"
    restart: always
    volumes:
      - pgadmin:/root/.pgadmin

volumes:
  database-data:
  pgadmin:

Then execute this command in the terminal

docker-compose up -d

It will takes sometime, because it downloads the image for PostgreSQL and PGAdmin. When you see this message, then it means it’s done.

Advertisements

Go to this url to login to PGAdmin http://localhost:5050/login use the credentials that we set earlier in the docker file. Email: pgadmin4@pgadmin.org and Password: admin1234

If everything goes well, you should be able to login to PGAdmin

Now let’s connect to our database. Right click to Servers then click Create then Server.

Then you will got this window. In the Name, you can put whatever name you want but for me I will name it ProductDb

Then click the Connection tab. The contents of this tab is what important, because we will be using this to connect to our database. Populate the fields with the value that we set earlier in our docker file.

Host name: postgresql_database

Port: 5432

database: productsDb

Username: admin

Password: admin1234

Now you should have this

Advertisements

Now we are done setting up our database. Time to install entity framework. Execute this command in the terminal.

dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 5.0.2

Then we also need to install dotnet-ef tool, to use dotnet migrations. To do that execute this to terminal

dotnet tool install --global dotnet-ef

You only need to install the dotnet tool once, the next time you create another project dotnet tool is already installed. but if you are not sure, you can try to install it again.

Now we need to install the entityframework design. Execute this command

dotnet add package Microsoft.EntityFrameworkCore.Design --version 5.0.5

Add a new folder in ProductsAPI called Models

Right click to Models folder then click New C# class

Name the class as Product.cs then press enter. After that you will have this.

Update the code for Product class to this
using System;

namespace ProductsApi.Models
{
    public class Product
    {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
        public DateTime DateCreated { get; set; }

    }
}
Advertisements

Now add a folder in ProductsAPI called Data. Then inside the Data folder add new interface called IDataContext

using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ProductsApi.Models;

namespace ProductsApi.Data
{
    public interface IDataContext
    {
         DbSet<Product> Products { get; init; }
        Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
    }
}

Then add a new class called DataContext. We will use the DataContext to query and save data to our database.

using Microsoft.EntityFrameworkCore;
using ProductsApi.Models;

namespace ProductsApi.Data
{
    public class DataContext: DbContext, IDataContext
    {
        public DataContext(DbContextOptions<DataContext> options) : base(options)
        {
            
        }

        public DbSet<Product> Products { get; init; }
    }
}

Update the appsettings.json to include the postgresql connection string.

{
  "ConnectionStrings": {
  "DefaultConnection": "Host=localhost;Port=5432;Database=productsDb;Username=admin;Password=admin1234"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Open the Startup.cs and update the code for the method ConfigureServices to this

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<DataContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
            services.AddScoped<IDataContext>(provider => provider.GetService<DataContext>());
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ProductsApi", Version = "v1" });
            });
        }

You also need to add these references in Startup.cs file

using ProductsApi.Data;
using Microsoft.EntityFrameworkCore;

Time to add our initial migration. Run this command to the terminal.

dotnet ef migrations add InitialMigration

After executing that command it will create this folder in our project.

Now run the command to update the database.

dotnet ef database update

Now go to PGAdmin and look at the productsDb, It should now have two tables. Products and migrations table.

Advertisements

Create a new folder in our project called Repositoies. Then create a new interface called IProductRepository

using System.Collections.Generic;
using System.Threading.Tasks;
using ProductsApi.Models;

namespace ProductsApi.Repositories
{
    public interface IProductRepository
    {
        Task<Product> Get(int id);
        Task<IEnumerable<Product>> GetAll();
        Task Add(Product product);
        Task Delete(int id);
        Task Update(Product product);
    }
}

Then create a new class called ProductRepository that will implement the IProductRepository

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ProductsApi.Data;
using ProductsApi.Models;

namespace ProductsApi.Repositories
{
  public class ProductRepository : IProductRepository
  {
    private readonly IDataContext _context;
    public ProductRepository(IDataContext context)
    {
      _context = context;

    }
    public async Task Add(Product product)
    {        
      _context.Products.Add(product);
      await _context.SaveChangesAsync();
    }

    public async Task Delete(int id)
    {
        var itemToRemove = await _context.Products.FindAsync(id);
        if (itemToRemove == null)
            throw new NullReferenceException();
        
        _context.Products.Remove(itemToRemove);
        await _context.SaveChangesAsync();
    }

    public async Task<Product> Get(int id)
    {
        return await _context.Products.FindAsync(id);
    }

    public async Task<IEnumerable<Product>> GetAll()
    {
        return await _context.Products.ToListAsync();
    }

    public async Task Update(Product product)
    {
        var itemToUpdate = await _context.Products.FindAsync(product.ProductId);
        if (itemToUpdate == null)
            throw new NullReferenceException();
        itemToUpdate.Name = product.Name;
        itemToUpdate.Price = product.Price;
        await _context.SaveChangesAsync();

    }
  }
}

Now let’s add this to our Startup.cs file. Update the ConfigureServices to this

public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<DataContext>(options => options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
            services.AddScoped<IDataContext>(provider => provider.GetService<DataContext>());
            services.AddScoped<IProductRepository, ProductRepository>();
            services.AddControllers();
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "ProductsApi", Version = "v1" });
            });
        }
Advertisements

Let’s create our controller. Add a new class in Controllers folder called ProductsController

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using ProductsApi.Dtos;
using ProductsApi.Models;
using ProductsApi.Repositories;

namespace ProductsApi.Controllers
{
  [ApiController]
  [Route("[controller]")]
  public class ProductsController : ControllerBase
  {
    private readonly IProductRepository _productRepository;
    public ProductsController(IProductRepository productRepository)
    {
      _productRepository = productRepository;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<Product>>> GetProducts()
    {
        var products = await _productRepository.GetAll();
        return Ok(products);
    }

    [HttpGet("{id}")]
    public async Task<ActionResult<Product>> GetProduct(int id)
    {
        var product = await _productRepository.Get(id);
        if(product == null)
            return NotFound();

        return Ok(product);
    }

    [HttpPost]
    public async Task<ActionResult> CreateProduct(CreateProductDto createProductDto)
    {
        Product product = new()
        {
            Name = createProductDto.Name,
            Price = createProductDto.Price,
            DateCreated = DateTime.Now
        };

        await _productRepository.Add(product);
        return Ok();
    }

    [HttpDelete("{id}")]
    public async Task<ActionResult> DeleteProduct(int id)
    {
        await _productRepository.Delete(id);
        return Ok();
    }

    [HttpPut("{id}")]
    public async Task<ActionResult> UpdateProduct(int id, UpdateProductDto updateProductDto)
    {
        Product product = new()
        {
            ProductId = id,
            Name = updateProductDto.Name,
            Price = updateProductDto.Price
        };

        await _productRepository.Update(product);
        return Ok();
    }
  }
}
Advertisements

In the ProductsController we use a Dto, so let’s add them Create a new folder called Dtos. Then a new class called CreateProductDto.

namespace ProductsApi.Dtos
{
    public class CreateProductDto
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

Add another class for our UpdateProductDto

namespace ProductsApi.Dtos
{
    public class UpdateProductDto
    {        
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

Now run our project and navigate to https://localhost:5001/swagger/index.html

You should now have this. Try our API to check if everything works.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s