Add book queries

This commit is contained in:
Alex Hyett 2023-07-03 14:50:35 +01:00
parent 146f8f6ff4
commit db9f7348fa
12 changed files with 176 additions and 83 deletions

18
docker-compose.yml Normal file
View file

@ -0,0 +1,18 @@
version: '3.8'
services:
db:
image: mysql:8.0
cap_add:
- SYS_NICE
restart: always
environment:
- MYSQL_DATABASE=library
- MYSQL_ROOT_PASSWORD=libraryDbPassword
ports:
- '3306:3306'
volumes:
- db:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
db:
driver: local

View file

@ -0,0 +1,30 @@
using GitHubActionsDemo.Api.Models;
using GitHubActionsDemo.Api.Mappers;
using GitHubActionsDemo.Service;
using Microsoft.AspNetCore.Mvc;
namespace GitHubActionsDemo.Api.Controllers;
[ApiController]
[Route("[controller]")]
public class AuthorsController : ControllerBase
{
private readonly ILogger<AuthorsController> _logger;
private readonly ILibraryService _libraryService;
public AuthorsController(
ILogger<AuthorsController> logger,
ILibraryService libraryService
)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService));
}
[HttpPost]
public async Task<AuthorResponse> AddAuthorAsync(AuthorRequest authorRequest)
{
var author = await _libraryService.AddAuthorAsync(authorRequest.Map());
return author.Map();
}
}

View file

@ -7,13 +7,13 @@ namespace GitHubActionsDemo.Api.Controllers;
[ApiController]
[Route("[controller]")]
public class LibraryController : ControllerBase
public class BooksController : ControllerBase
{
private readonly ILogger<LibraryController> _logger;
private readonly ILogger<BooksController> _logger;
private readonly ILibraryService _libraryService;
public LibraryController(
ILogger<LibraryController> logger,
public BooksController(
ILogger<BooksController> logger,
ILibraryService libraryService
)
{
@ -21,31 +21,24 @@ public class LibraryController : ControllerBase
_libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService));
}
[HttpGet(Name = "GetBooks")]
[HttpGet]
public async Task<IEnumerable<BookResponse>> GetBooksAsync(int page = 0, int pageSize = 10)
{
var books = await _libraryService.GetBooksAsync(page, pageSize);
return books.Select(x => x.Map());
}
[HttpGet(Name = "GetBook")]
[HttpGet("{bookId}")]
public async Task<BookResponse> GetBookAsync(int bookId)
{
var book = await _libraryService.GetBookAsync(bookId);
return book.Map();
}
[HttpPost(Name = "AddBook")]
[HttpPost]
public async Task<BookResponse> AddBookAsync(BookRequest bookRequest)
{
var book = await _libraryService.AddBookAsync(bookRequest.Map());
return book.Map();
}
[HttpPost(Name = "AddAuthor")]
public async Task<AuthorResponse> AddAuthorAsync(AuthorRequest authorRequest)
{
var author = await _libraryService.AddAuthorAsync(authorRequest.Map());
return author.Map();
}
}

View file

@ -1,6 +1,6 @@
{
"DbSettings": {
"ConnectionString": "Server=localhost; Database=library; Uid=root; Pwd=libraryDbPassword;",
"ConnectionString": "Server=localhost:3306; Database=library; Uid=root; Pwd=libraryDbPassword;",
"Database": "library"
},
"Logging": {

View file

@ -44,13 +44,13 @@ public class DbContext : IDbContext
async Task _initAuthors()
{
var sql = """
CREATE TABLE IF NOT EXISTS Authors (
AuthorId INT NOT NULL AUTO_INCREMENT,
FirstName VARCHAR(255) NOT NULL,
LastName VARCHAR(255) NOT NULL,
DateCreated DATETIME NOT NULL,
DateModified DATETIME NOT NULL,
PRIMARY KEY (AuthorId)
CREATE TABLE IF NOT EXISTS authors (
author_id INT NOT NULL AUTO_INCREMENT,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
date_created DATETIME NOT NULL,
date_modified DATETIME NOT NULL,
PRIMARY KEY (author_id)
);
""";
await connection.ExecuteAsync(sql);
@ -59,16 +59,16 @@ public class DbContext : IDbContext
async Task _initBooks()
{
var sql = """
CREATE TABLE IF NOT EXISTS Books(
BookId INT NOT NULL AUTO_INCREMENT,
Title VARCHAR(255),
AuthorId INT NOT NULL,
Isbn VARCHAR(13) NOT NULL,
DatePublished DATETIME NOT NULL,
DateCreated DATETIME NOT NULL,
DateModified DATETIME NOT NULL,
PRIMARY KEY(BookId),
FOREIGN KEY(AuthorId) REFERENCES Authors(AuthorId)
CREATE TABLE IF NOT EXISTS books(
book_id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255),
author_id INT NOT NULL,
isbn VARCHAR(13) NOT NULL,
date_published DATETIME NOT NULL,
date_created DATETIME NOT NULL,
date_modified DATETIME NOT NULL,
PRIMARY KEY(book_id),
FOREIGN KEY(author_id) REFERENCES authors(author_id)
);
""";
await connection.ExecuteAsync(sql);

View file

@ -8,6 +8,7 @@
<ItemGroup>
<PackageReference Include="Dapper" Version="2.0.143" />
<PackageReference Include="Dapper.FluentMap" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.5" />
<PackageReference Include="MySql.Data" Version="8.0.33" />
</ItemGroup>

View file

@ -1,3 +1,4 @@
using Dapper.FluentMap;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
@ -16,5 +17,11 @@ public static class InfrastructureExtensions
using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<IDbContext>();
await context.Init();
FluentMapper.Initialize(config =>
{
config.AddMap(new AuthorDbMap());
config.AddMap(new BookDbMap());
});
}
}

View file

@ -1,12 +1,15 @@
using GitHubActionsDemo.Persistance.Models;
using System;
using Dapper;
using GitHubActionsDemo.Persistance.Models;
namespace GitHubActionsDemo.Persistance;
public class LibraryRespository : ILibraryRespository
{
public LibraryRespository()
private readonly IDbContext _dbContext;
public LibraryRespository(IDbContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task<AuthorDb> AddAuthorAsync(NewAuthorDb author)
@ -26,11 +29,64 @@ public class LibraryRespository : ILibraryRespository
public async Task<BookDb> GetBookAsync(int bookId)
{
throw new NotImplementedException();
var sql = @$"SELECT
b.book_id AS {nameof(BookDb.BookId)},
b.title AS {nameof(BookDb.Title)},
b.isbn AS {nameof(BookDb.Isbn)},
b.date_published AS {nameof(BookDb.DatePublished)},
b.date_created AS Book{nameof(BookDb.DateCreated)},
b.date_modified AS Book{nameof(BookDb.DateModified)},
a.author_id AS {nameof(AuthorDb.AuthorId)},
a.first_name AS {nameof(AuthorDb.FirstName)},
a.last_name AS {nameof(AuthorDb.LastName)},
a.date_created AS Author{nameof(AuthorDb.DateCreated)},
a.date_modified AS Author{nameof(AuthorDb.DateModified)}
FROM books b
INNER JOIN authors a ON a.author_id = b.author_id
WHERE b.book_id = @BookId;";
var param = new
{
BookId = bookId
};
using (var connection = _dbContext.CreateConnection())
{
var books = await connection.QueryAsync<BookDb, AuthorDb, BookDb>(sql, (book, author) =>
{
book.Author = author;
return book;
}, param, splitOn: nameof(AuthorDb.AuthorId));
return books?.FirstOrDefault();
}
}
public async Task<IEnumerable<BookDb>> GetBooksAsync(int page, int pageSize)
{
throw new NotImplementedException();
var sql = @$"SELECT
b.book_id AS {nameof(BookDb.BookId)},
b.title AS {nameof(BookDb.Title)},
b.isbn AS {nameof(BookDb.Isbn)},
b.date_published AS {nameof(BookDb.DatePublished)},
b.date_created AS Book{nameof(BookDb.DateCreated)},
b.date_modified AS Book{nameof(BookDb.DateModified)},
a.author_id AS {nameof(AuthorDb.AuthorId)},
a.first_name AS {nameof(AuthorDb.FirstName)},
a.last_name AS {nameof(AuthorDb.LastName)},
a.date_created AS Author{nameof(AuthorDb.DateCreated)},
a.date_modified AS Author{nameof(AuthorDb.DateModified)}
FROM books b
INNER JOIN authors a ON a.author_id = b.author_id
ORDER BY b.book_id;";
using (var connection = _dbContext.CreateConnection())
{
return await connection.QueryAsync<BookDb, AuthorDb, BookDb>(sql, (book, author) =>
{
book.Author = author;
return book;
}, splitOn: nameof(AuthorDb.AuthorId));
}
}
}

View file

@ -0,0 +1,11 @@
using Dapper.FluentMap.Mapping;
using GitHubActionsDemo.Persistance.Models;
internal class AuthorDbMap : EntityMap<AuthorDb>
{
internal AuthorDbMap()
{
Map(u => u.DateCreated).ToColumn($"Author{nameof(AuthorDb.DateCreated)}");
Map(u => u.DateModified).ToColumn($"Author{nameof(AuthorDb.DateModified)}");
}
}

View file

@ -0,0 +1,11 @@
using Dapper.FluentMap.Mapping;
using GitHubActionsDemo.Persistance.Models;
internal class BookDbMap : EntityMap<BookDb>
{
internal BookDbMap()
{
Map(u => u.DateCreated).ToColumn($"Book{nameof(BookDb.DateCreated)}");
Map(u => u.DateModified).ToColumn($"Book{nameof(BookDb.DateModified)}");
}
}

View file

@ -2,24 +2,9 @@ namespace GitHubActionsDemo.Persistance.Models;
public class AuthorDb
{
public AuthorDb(
int authorId,
string firstName,
string lastName,
DateTime dateCreated,
DateTime dateModified
)
{
AuthorId = authorId;
FirstName = firstName;
LastName = lastName;
DateCreated = dateCreated;
DateModified = dateModified;
}
public int AuthorId { get; }
public string FirstName { get; }
public string LastName { get; }
public DateTime DateCreated { get; }
public DateTime DateModified { get; }
public int AuthorId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
}

View file

@ -2,30 +2,11 @@ namespace GitHubActionsDemo.Persistance.Models;
public class BookDb
{
public BookDb(
int bookId,
string title,
AuthorDb author,
string isbn,
DateOnly datePublished,
DateTime dateCreated,
DateTime dateModified
)
{
BookId = bookId;
Title = title;
Author = author;
Isbn = isbn;
DatePublished = datePublished;
DateCreated = dateCreated;
DateModified = dateModified;
}
public int BookId { get; }
public string Title { get; }
public AuthorDb Author { get; }
public string Isbn { get; }
public DateOnly DatePublished { get; }
public DateTime DateCreated { get; }
public DateTime DateModified { get; }
public int BookId { get; set; }
public string Title { get; set; }
public AuthorDb Author { get; set; }
public string Isbn { get; set; }
public DateOnly DatePublished { get; set; }
public DateTime DateCreated { get; set; }
public DateTime DateModified { get; set; }
}