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] [ApiController]
[Route("[controller]")] [Route("[controller]")]
public class LibraryController : ControllerBase public class BooksController : ControllerBase
{ {
private readonly ILogger<LibraryController> _logger; private readonly ILogger<BooksController> _logger;
private readonly ILibraryService _libraryService; private readonly ILibraryService _libraryService;
public LibraryController( public BooksController(
ILogger<LibraryController> logger, ILogger<BooksController> logger,
ILibraryService libraryService ILibraryService libraryService
) )
{ {
@ -21,31 +21,24 @@ public class LibraryController : ControllerBase
_libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService)); _libraryService = libraryService ?? throw new ArgumentNullException(nameof(libraryService));
} }
[HttpGet(Name = "GetBooks")] [HttpGet]
public async Task<IEnumerable<BookResponse>> GetBooksAsync(int page = 0, int pageSize = 10) public async Task<IEnumerable<BookResponse>> GetBooksAsync(int page = 0, int pageSize = 10)
{ {
var books = await _libraryService.GetBooksAsync(page, pageSize); var books = await _libraryService.GetBooksAsync(page, pageSize);
return books.Select(x => x.Map()); return books.Select(x => x.Map());
} }
[HttpGet(Name = "GetBook")] [HttpGet("{bookId}")]
public async Task<BookResponse> GetBookAsync(int bookId) public async Task<BookResponse> GetBookAsync(int bookId)
{ {
var book = await _libraryService.GetBookAsync(bookId); var book = await _libraryService.GetBookAsync(bookId);
return book.Map(); return book.Map();
} }
[HttpPost(Name = "AddBook")] [HttpPost]
public async Task<BookResponse> AddBookAsync(BookRequest bookRequest) public async Task<BookResponse> AddBookAsync(BookRequest bookRequest)
{ {
var book = await _libraryService.AddBookAsync(bookRequest.Map()); var book = await _libraryService.AddBookAsync(bookRequest.Map());
return book.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": { "DbSettings": {
"ConnectionString": "Server=localhost; Database=library; Uid=root; Pwd=libraryDbPassword;", "ConnectionString": "Server=localhost:3306; Database=library; Uid=root; Pwd=libraryDbPassword;",
"Database": "library" "Database": "library"
}, },
"Logging": { "Logging": {

View file

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

View file

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

View file

@ -1,3 +1,4 @@
using Dapper.FluentMap;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -16,5 +17,11 @@ public static class InfrastructureExtensions
using var scope = app.Services.CreateScope(); using var scope = app.Services.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<IDbContext>(); var context = scope.ServiceProvider.GetRequiredService<IDbContext>();
await context.Init(); 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; namespace GitHubActionsDemo.Persistance;
public class LibraryRespository : ILibraryRespository 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) public async Task<AuthorDb> AddAuthorAsync(NewAuthorDb author)
@ -26,11 +29,64 @@ public class LibraryRespository : ILibraryRespository
public async Task<BookDb> GetBookAsync(int bookId) 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) 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 class AuthorDb
{ {
public AuthorDb( public int AuthorId { get; set; }
int authorId, public string FirstName { get; set; }
string firstName, public string LastName { get; set; }
string lastName, public DateTime DateCreated { get; set; }
DateTime dateCreated, public DateTime DateModified { get; set; }
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; }
} }

View file

@ -2,30 +2,11 @@ namespace GitHubActionsDemo.Persistance.Models;
public class BookDb public class BookDb
{ {
public BookDb( public int BookId { get; set; }
int bookId, public string Title { get; set; }
string title, public AuthorDb Author { get; set; }
AuthorDb author, public string Isbn { get; set; }
string isbn, public DateOnly DatePublished { get; set; }
DateOnly datePublished, public DateTime DateCreated { get; set; }
DateTime dateCreated, public DateTime DateModified { get; set; }
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; }
} }