Creating of git for project ApiEfCoreLibrary

This commit is contained in:
2025-10-13 12:54:37 +02:00
commit 74878c1536
51 changed files with 1866 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
bin/
obj/
/packages/
riderModule.iml
/_ReSharper.Caches/

16
ApiEfCoreLibrary.sln Normal file
View File

@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiEfCoreLibrary", "ApiEfCoreLibrary\ApiEfCoreLibrary.csproj", "{E7D4AEB5-54C1-429D-8B2F-CAE960763823}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E7D4AEB5-54C1-429D-8B2F-CAE960763823}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E7D4AEB5-54C1-429D-8B2F-CAE960763823}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E7D4AEB5-54C1-429D-8B2F-CAE960763823}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E7D4AEB5-54C1-429D-8B2F-CAE960763823}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FastEndpoints" Version="7.0.1" />
<PackageReference Include="FastEndpoints.Security" Version="7.0.1" />
<PackageReference Include="FastEndpoints.Swagger" Version="7.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.19"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.20">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.20" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2"/>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
@ApiEfCoreLibrary_HostAddress = http://localhost:5228
GET {{ApiEfCoreLibrary_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -0,0 +1,7 @@
namespace ApiEfCoreLibrary.DTO.Author.Request;
public class CreateAuthorDto
{
public string? Name { get; set; }
public string? FirstName { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace ApiEfCoreLibrary.DTO.Author.Request;
public class UpdateAuthorDto
{
public int Id { get; set; }
public string? Name { get; set; }
public string? FirstName { get; set; }
}

View File

@@ -0,0 +1,11 @@
using ApiEfCoreLibrary.DTO.Book.Response;
namespace ApiEfCoreLibrary.DTO.Author.Response;
public class GetAuthorDto
{
public int Id { get; set; }
public string? Name { get; set; }
public string? FirstName { get; set; }
public List<GetBookDto>? Books { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace ApiEfCoreLibrary.DTO.Book.Request;
public class CreateBookDto
{
public string? Title { get; set; }
public int? ReleaseYear { get; set; }
public string? Isbn { get; set; }
public int AuthorId { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace ApiEfCoreLibrary.DTO.Book.Request;
public class UpdateBookDto
{
public int Id { get; set; }
public string? Title { get; set; }
public int? ReleaseYear { get; set; }
public string? Isbn { get; set; }
public int AuthorId { get; set; }
}

View File

@@ -0,0 +1,12 @@
namespace ApiEfCoreLibrary.DTO.Book.Response;
public class GetBookDto
{
public int Id { get; set; }
public string? Title { get; set; }
public int AuthorId { get; set; }
public int BookAuthorId { get; set; }
public string? BookAuthorName { get; set; }
public string? BookAuthorFirstName { get; set; }
public int? ReleaseYear { get; set; }
public string? Isbn { get; set; }
}

View File

@@ -0,0 +1,8 @@
namespace ApiEfCoreLibrary.DTO.Loan.Request;
public class CreateLoanDto
{
public int BookId { get; set; }
public int UserId { get; set; }
public DateOnly Date { get; set; }
}

View File

@@ -0,0 +1,7 @@
namespace ApiEfCoreLibrary.DTO.Loan.Request;
public class PatchLoanDto
{
public int Id { get; set; }
public DateOnly EffectiveReturningDate { get; set; }
}

View File

@@ -0,0 +1,11 @@
namespace ApiEfCoreLibrary.DTO.Loan.Request;
public class UpdateLoanDto
{
public int Id { get; set; }
public int BookId { get; set; }
public int UserId { get; set; }
public DateOnly Date { get; set; }
public DateOnly PlannedReturningDate { get; set; }
public DateOnly? EffectiveReturningDate { get; set; }
}

View File

@@ -0,0 +1,23 @@
namespace ApiEfCoreLibrary.DTO.Loan.Response;
public class GetLoanDto
{
public int Id { get; set; }
public int BookId { get; set; }
public string? BookTitle { get; set; }
public int BookAuthorId { get; set; }
public string? BookAuthorName { get; set; }
public string? BookAuthorFirstName { get; set; }
public int? BookReleaseYear { get; set; }
public string? BookIsbn { get; set; }
public int UserId { get; set; }
public string? UserName { get; set; }
public string? UserFirstName { get; set; }
public string? UserEmail { get; set; }
public DateOnly? UserBirthDate { get; set; }
public DateOnly Date { get; set; }
public DateOnly PlannedReturningDate { get; set; }
public DateOnly? EffectiveReturningDate { get; set; }
}

View File

@@ -0,0 +1,9 @@
namespace ApiEfCoreLibrary.DTO.User.Request;
public class CreateUserDto
{
public string? Name { get; set; }
public string? FirstName { get; set; }
public string? Email { get; set; }
public DateOnly? BirthDate { get; set; }
}

View File

@@ -0,0 +1,10 @@
namespace ApiEfCoreLibrary.DTO.User.Request;
public class UpdateUserDto
{
public int Id { get; set; }
public string? Name { get; set; }
public string? FirstName { get; set; }
public string? Email { get; set; }
public DateOnly? BirthDate { get; set; }
}

View File

@@ -0,0 +1,13 @@
using ApiEfCoreLibrary.DTO.Loan.Response;
namespace ApiEfCoreLibrary.DTO.User.Response;
public class GetUserDto
{
public int Id { get; set; }
public string? Name { get; set; }
public string? FirstName { get; set; }
public string? Email { get; set; }
public DateOnly? BirthDate { get; set; }
public List<GetLoanDto>? Loans { get; set; }
}

View File

@@ -0,0 +1,37 @@
using ApiEfCoreLibrary.DTO.Author.Request;
using ApiEfCoreLibrary.DTO.Author.Response;
namespace ApiEfCoreLibrary.Endpoints.Author;
using FastEndpoints;
public class CreateAuthorEndpoint(LibraryDbContext database) : Endpoint<CreateAuthorDto, GetAuthorDto>
{
public override void Configure()
{
Post("/api/authors");
AllowAnonymous();
}
public override async Task HandleAsync(CreateAuthorDto req, CancellationToken ct)
{
var author = new Models.Author()
{
Name = req.Name,
FirstName = req.FirstName
};
database.Authors.Add(author);
await database.SaveChangesAsync(ct);
// Pour renvoyer une erreur : Send.StringAsync("Le message d'erreur", 400);
GetAuthorDto responseDto = new()
{
Id = author.Id,
Name = author.Name,
FirstName = author.FirstName
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,36 @@
using ApiEfCoreLibrary.DTO.Author.Request;
using ApiEfCoreLibrary.DTO.Author.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Author;
public class DeleteAuthorRequest
{
public int Id { get; set; }
}
public class DeleteAuthorEndpoint(LibraryDbContext database) : Endpoint<DeleteAuthorRequest>
{
public override void Configure()
{
Delete("/api/authors/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(DeleteAuthorRequest req, CancellationToken ct)
{
var author = await database.Authors.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (author == null)
{
await Send.NotFoundAsync(ct);
return;
}
database.Authors.Remove(author);
await database.SaveChangesAsync(ct);
await Send.NoContentAsync(ct);
}
}

View File

@@ -0,0 +1,40 @@
using ApiEfCoreLibrary.DTO.Author.Response;
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Author;
public class GetAllAuthorsEndpoint(LibraryDbContext database) : EndpointWithoutRequest<List<GetAuthorDto>>
{
public override void Configure()
{
Get("/api/authors");
AllowAnonymous();
}
public override async Task HandleAsync(CancellationToken ct)
{
var authors = await database.Authors
.Include(x => x.Books)
.Select(author => new GetAuthorDto()
{
Id = author.Id,
Name = author.Name,
FirstName = author.FirstName,
Books = author.Books.Select(book => new GetBookDto
{
Id = book.Id,
Title = book.Title,
AuthorId = book.AuthorId,
BookAuthorName = book.Author.Name,
BookAuthorFirstName = book.Author.FirstName,
ReleaseYear = book.ReleaseYear,
Isbn = book.Isbn
}).ToList()
})
.ToListAsync(ct);
await Send.OkAsync(authors, ct);
}
}

View File

@@ -0,0 +1,53 @@
using ApiEfCoreLibrary.DTO.Author.Request;
using ApiEfCoreLibrary.DTO.Author.Response;
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Author;
public class GetAuthorRequest
{
public int Id { get; set; }
}
public class GetAuthorEndpoint(LibraryDbContext database) : Endpoint<GetAuthorRequest, GetAuthorDto>
{
public override void Configure()
{
Get("/api/authors/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(GetAuthorRequest req, CancellationToken ct)
{
var author = await database.Authors
.Include(x => x.Books)
.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (author == null)
{
await Send.NotFoundAsync(ct);
return;
}
GetAuthorDto responseDto = new()
{
Id = author.Id,
Name = author.Name,
FirstName = author.FirstName,
Books = author.Books.Select(book => new GetBookDto
{
Id = book.Id,
Title = book.Title,
AuthorId = book.AuthorId,
BookAuthorName = book.Author.Name,
BookAuthorFirstName = book.Author.FirstName,
ReleaseYear = book.ReleaseYear,
Isbn = book.Isbn
}).ToList()
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,40 @@
using ApiEfCoreLibrary.DTO.Author.Request;
using ApiEfCoreLibrary.DTO.Author.Response;
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Author;
public class UpdateAuthorEndpoint(LibraryDbContext database) : Endpoint<UpdateAuthorDto, GetAuthorDto>
{
public override void Configure()
{
Put("/api/authors/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(UpdateAuthorDto req, CancellationToken ct)
{
var author = await database.Authors.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (author == null)
{
await Send.NotFoundAsync(ct);
return;
}
author.Name = req.Name;
author.FirstName = req.FirstName;
await database.SaveChangesAsync(ct);
GetAuthorDto responseDto = new()
{
Id = author.Id,
Name = author.Name,
FirstName = author.FirstName,
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,52 @@
using ApiEfCoreLibrary.DTO.Book.Request;
using ApiEfCoreLibrary.DTO.Book.Response;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Book;
using FastEndpoints;
public class CreateBookEndpoint(LibraryDbContext database) : Endpoint<CreateBookDto, GetBookDto>
{
public override void Configure()
{
Post("/api/books");
AllowAnonymous();
}
public override async Task HandleAsync(CreateBookDto req, CancellationToken ct)
{
var authorExists = await database.Authors.FirstOrDefaultAsync(a => a.Id == req.AuthorId, ct);
if (authorExists == null)
{
await Send.NoContentAsync(ct);
return;
}
var book = new Models.Book()
{
Title = req.Title,
AuthorId = req.AuthorId,
ReleaseYear = req.ReleaseYear,
Isbn = req.Isbn
};
database.Books.Add(book);
await database.SaveChangesAsync(ct);
// Pour renvoyer une erreur : Send.StringAsync("Le message d'erreur", 400);
GetBookDto responseDto = new()
{
Id = book.Id,
Title = book.Title,
BookAuthorId = book.AuthorId,
BookAuthorName = book.Author.Name,
BookAuthorFirstName = book.Author.FirstName,
ReleaseYear = book.ReleaseYear,
Isbn = book.Isbn
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,37 @@
using ApiEfCoreLibrary.DTO.Author.Request;
using ApiEfCoreLibrary.DTO.Author.Response;
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Book;
public class DeleteBookRequest
{
public int Id { get; set; }
}
public class DeleteBookEndpoint(LibraryDbContext database) : Endpoint<DeleteBookRequest>
{
public override void Configure()
{
Delete("/api/books/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(DeleteBookRequest req, CancellationToken ct)
{
var book = await database.Books.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (book == null)
{
await Send.NotFoundAsync(ct);
return;
}
database.Books.Remove(book);
await database.SaveChangesAsync(ct);
await Send.NoContentAsync(ct);
}
}

View File

@@ -0,0 +1,33 @@
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Book;
public class GetAllBooksEndpoint(LibraryDbContext database) : EndpointWithoutRequest<List<GetBookDto>>
{
public override void Configure()
{
Get("/api/books");
AllowAnonymous();
}
public override async Task HandleAsync(CancellationToken ct)
{
var books = await database.Books
.Include(b => b.Author)
.Select(book => new GetBookDto()
{
Id = book.Id,
Title = book.Title,
BookAuthorId = book.AuthorId,
BookAuthorName = book.Author.Name,
BookAuthorFirstName = book.Author.FirstName,
ReleaseYear = book.ReleaseYear,
Isbn = book.Isbn
})
.ToListAsync(ct);
await Send.OkAsync(books, ct);
}
}

View File

@@ -0,0 +1,43 @@
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Book;
public class GetBookRequest
{
public int Id { get; set; }
}
public class GetBookEndpoint(LibraryDbContext database) : Endpoint<GetBookRequest, GetBookDto>
{
public override void Configure()
{
Get("/api/books/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(GetBookRequest req, CancellationToken ct)
{
var book = await database.Books.Include(b => b.Author).SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (book == null)
{
await Send.NotFoundAsync(ct);
return;
}
GetBookDto responseDto = new()
{
Id = book.Id,
Title = book.Title,
BookAuthorId = book.AuthorId,
BookAuthorName = book.Author.Name,
BookAuthorFirstName = book.Author.FirstName,
ReleaseYear = book.ReleaseYear,
Isbn = book.Isbn
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,53 @@
using ApiEfCoreLibrary.DTO.Book.Request;
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Book;
public class UpdateBookEndpoint(LibraryDbContext database) : Endpoint<UpdateBookDto, GetBookDto>
{
public override void Configure()
{
Put("/api/books/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(UpdateBookDto req, CancellationToken ct)
{
var book = await database.Books.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (book == null)
{
await Send.NotFoundAsync(ct);
return;
}
var authorExists = await database.Authors.FirstOrDefaultAsync(a => a.Id == req.AuthorId, ct);
if (authorExists == null)
{
await Send.NoContentAsync(ct);
return;
}
book.Title = req.Title;
book.AuthorId = req.AuthorId;
book.ReleaseYear = req.ReleaseYear;
book.Isbn = req.Isbn;
await database.SaveChangesAsync(ct);
GetBookDto responseDto = new()
{
Id = book.Id,
Title = book.Title,
BookAuthorId = book.AuthorId,
BookAuthorName = book.Author.Name,
BookAuthorFirstName = book.Author.FirstName,
ReleaseYear = book.ReleaseYear,
Isbn = book.Isbn
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,58 @@
using ApiEfCoreLibrary.DTO.Loan.Request;
using ApiEfCoreLibrary.DTO.Loan.Response;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Loan;
using FastEndpoints;
public class CreateLoanEndpoint(LibraryDbContext database) : Endpoint<CreateLoanDto, GetLoanDto>
{
public override void Configure()
{
Post("/api/loans");
AllowAnonymous();
}
public override async Task HandleAsync(CreateLoanDto req, CancellationToken ct)
{
var bookExists = await database.Books.FirstOrDefaultAsync(a => a.Id == req.BookId, ct);
if (bookExists == null)
{
await Send.NoContentAsync(ct);
return;
}
var userExists = await database.Users.FirstOrDefaultAsync(a => a.Id == req.UserId, ct);
if (userExists == null)
{
await Send.NoContentAsync(ct);
return;
}
var loan = new Models.Loan()
{
BookId = req.BookId,
UserId = req.UserId,
Date = req.Date,
PlannedReturningDate = req.Date.AddMonths(2)
};
database.Loans.Add(loan);
await database.SaveChangesAsync(ct);
// Pour renvoyer une erreur : Send.StringAsync("Le message d'erreur", 400);
GetLoanDto responseDto = new()
{
Id = loan.Id,
BookId = loan.BookId,
UserId = loan.UserId,
Date = loan.Date,
PlannedReturningDate = loan.PlannedReturningDate
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,37 @@
using ApiEfCoreLibrary.DTO.Loan.Request;
using ApiEfCoreLibrary.DTO.Loan.Response;
using ApiEfCoreLibrary.DTO.Book.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Loan;
public class DeleteLoanRequest
{
public int Id { get; set; }
}
public class DeleteLoanEndpoint(LibraryDbContext database) : Endpoint<DeleteLoanRequest>
{
public override void Configure()
{
Delete("/api/loans/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(DeleteLoanRequest req, CancellationToken ct)
{
var loan = await database.Loans.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (loan == null)
{
await Send.NotFoundAsync(ct);
return;
}
database.Loans.Remove(loan);
await database.SaveChangesAsync(ct);
await Send.NoContentAsync(ct);
}
}

View File

@@ -0,0 +1,44 @@
using ApiEfCoreLibrary.DTO.Loan.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Loan;
public class GetAllLoanEndpoint(LibraryDbContext database) : EndpointWithoutRequest<List<GetLoanDto>>
{
public override void Configure()
{
Get("/api/loans");
AllowAnonymous();
}
public override async Task HandleAsync(CancellationToken ct)
{
var loans = await database.Loans
.Include(l => l.Book)
.ThenInclude(b => b.Author)
.Include(l => l.User)
.Select(loan => new GetLoanDto()
{
Id = loan.Id,
BookId = loan.BookId,
BookTitle = loan.Book.Title,
BookAuthorId = loan.Book.AuthorId,
BookAuthorName = loan.Book.Author.Name,
BookAuthorFirstName = loan.Book.Author.FirstName,
BookReleaseYear = loan.Book.ReleaseYear,
BookIsbn = loan.Book.Isbn,
UserId = loan.UserId,
UserName = loan.User.Name,
UserFirstName = loan.User.FirstName,
UserEmail = loan.User.Email,
UserBirthDate = loan.User.BirthDate,
Date = loan.Date,
PlannedReturningDate = loan.PlannedReturningDate,
EffectiveReturningDate = loan.EffectiveReturningDate
})
.ToListAsync(ct);
await Send.OkAsync(loans, ct);
}
}

View File

@@ -0,0 +1,55 @@
using ApiEfCoreLibrary.DTO.Loan.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Loan;
public class GetLoanRequest
{
public int Id { get; set; }
}
public class GetLoanEndpoint(LibraryDbContext database) : Endpoint<GetLoanRequest, GetLoanDto>
{
public override void Configure()
{
Get("/api/loans/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(GetLoanRequest req, CancellationToken ct)
{
var loan = await database.Loans.Include(l => l.Book)
.ThenInclude(b => b.Author)
.Include(l => l.User)
.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (loan == null)
{
await Send.NotFoundAsync(ct);
return;
}
GetLoanDto responseDto = new()
{
Id = loan.Id,
BookId = loan.BookId,
BookTitle = loan.Book.Title,
BookAuthorId = loan.Book.AuthorId,
BookAuthorName = loan.Book.Author.Name,
BookAuthorFirstName = loan.Book.Author.FirstName,
BookReleaseYear = loan.Book.ReleaseYear,
BookIsbn = loan.Book.Isbn,
UserId = loan.UserId,
UserName = loan.User.Name,
UserFirstName = loan.User.FirstName,
UserEmail = loan.User.Email,
UserBirthDate = loan.User.BirthDate,
Date = loan.Date,
PlannedReturningDate = loan.PlannedReturningDate,
EffectiveReturningDate = loan.EffectiveReturningDate
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,53 @@
using ApiEfCoreLibrary.DTO.Loan.Request;
using ApiEfCoreLibrary.DTO.Loan.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Loan;
public class PatchLoanEndpoint(LibraryDbContext database) : Endpoint<PatchLoanDto, GetLoanDto>
{
public override void Configure()
{
Patch("/api/loans/{@Id}/EffectiveReturningDate", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(PatchLoanDto req, CancellationToken ct)
{
var loan = await database.Loans.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (loan == null)
{
await Send.NotFoundAsync(ct);
return;
}
if (req.EffectiveReturningDate <= DateOnly.FromDateTime(DateTime.Now))
{
await Send.StringAsync("Erreur de date. La date est inférieure à la date actuelle.", 400);
return;
}
if (loan.EffectiveReturningDate != null)
{
await Send.StringAsync("Impossible de modifier la date de retour.", 400);
return;
}
loan.EffectiveReturningDate = req.EffectiveReturningDate;
await database.SaveChangesAsync(ct);
GetLoanDto responseDto = new()
{
Id = loan.Id,
BookId = loan.BookId,
UserId = loan.UserId,
Date = loan.Date,
PlannedReturningDate = loan.PlannedReturningDate,
EffectiveReturningDate = loan.EffectiveReturningDate
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,61 @@
using ApiEfCoreLibrary.DTO.Loan.Request;
using ApiEfCoreLibrary.DTO.Loan.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.Loan;
public class UpdateLoanEndpoint(LibraryDbContext database) : Endpoint<UpdateLoanDto, GetLoanDto>
{
public override void Configure()
{
Put("/api/loans/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(UpdateLoanDto req, CancellationToken ct)
{
var loan = await database.Loans.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (loan == null)
{
await Send.NotFoundAsync(ct);
return;
}
var bookExists = await database.Books.FirstOrDefaultAsync(a => a.Id == req.BookId, ct);
if (bookExists == null)
{
await Send.NoContentAsync(ct);
return;
}
var userExists = await database.Users.FirstOrDefaultAsync(a => a.Id == req.UserId, ct);
if (userExists == null)
{
await Send.NoContentAsync(ct);
return;
}
loan.BookId = req.BookId;
loan.UserId = req.UserId;
loan.Date = req.Date;
loan.PlannedReturningDate = req.PlannedReturningDate;
loan.EffectiveReturningDate = req.EffectiveReturningDate;
await database.SaveChangesAsync(ct);
GetLoanDto responseDto = new()
{
Id = loan.Id,
BookId = loan.BookId,
UserId = loan.UserId,
Date = loan.Date,
PlannedReturningDate = loan.PlannedReturningDate,
EffectiveReturningDate = loan.EffectiveReturningDate
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,40 @@
using ApiEfCoreLibrary.DTO.User.Request;
using ApiEfCoreLibrary.DTO.User.Response;
namespace ApiEfCoreLibrary.Endpoints.User;
using FastEndpoints;
public class CreateUserEndpoint(LibraryDbContext database) : Endpoint<CreateUserDto, GetUserDto>
{
public override void Configure()
{
Post("/api/users");
AllowAnonymous();
}
public override async Task HandleAsync(CreateUserDto req, CancellationToken ct)
{
var user = new Models.User()
{
Name = req.Name,
FirstName = req.FirstName,
Email = req.Email,
BirthDate = req.BirthDate,
};
database.Users.Add(user);
await database.SaveChangesAsync(ct);
GetUserDto responseDto = new()
{
Id = user.Id,
Name = user.Name,
FirstName = user.FirstName,
Email = user.Email,
BirthDate = user.BirthDate
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,36 @@
using ApiEfCoreLibrary.DTO.User.Request;
using ApiEfCoreLibrary.DTO.User.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.User;
public class DeleteUserRequest
{
public int Id { get; set; }
}
public class DeleteUserEndpoint(LibraryDbContext database) : Endpoint<DeleteUserRequest>
{
public override void Configure()
{
Delete("/api/users/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(DeleteUserRequest req, CancellationToken ct)
{
var user = await database.Users.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (user == null)
{
await Send.NotFoundAsync(ct);
return;
}
database.Users.Remove(user);
await database.SaveChangesAsync(ct);
await Send.NoContentAsync(ct);
}
}

View File

@@ -0,0 +1,52 @@
using ApiEfCoreLibrary.DTO.Loan.Response;
using ApiEfCoreLibrary.DTO.User.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.User;
public class GetAllUsersEndpoint(LibraryDbContext database) : EndpointWithoutRequest<List<GetUserDto>>
{
public override void Configure()
{
Get("/api/users");
AllowAnonymous();
}
public override async Task HandleAsync(CancellationToken ct)
{
var user = await database.Users
.Include(x => x.Loans)!
.ThenInclude(l => l.Book)
.ThenInclude(b => b!.Author)
.Select(user => new GetUserDto()
{
Id = user.Id,
Name = user.Name,
FirstName = user.FirstName,
Email = user.Email,
BirthDate = user.BirthDate,
Loans = user.Loans.Select(loan => new GetLoanDto
{
Id = loan.Id,
BookId = loan.BookId,
BookTitle = loan.Book.Title,
BookAuthorId = loan.Book.AuthorId,
BookAuthorName = loan.Book.Author.Name,
BookAuthorFirstName = loan.Book.Author.FirstName,
BookReleaseYear = loan.Book.ReleaseYear,
BookIsbn = loan.Book.Isbn,
UserId = loan.UserId,
UserName = loan.User.Name,
UserFirstName = loan.User.FirstName,
UserEmail = loan.User.Email,
Date = loan.Date,
PlannedReturningDate = loan.PlannedReturningDate,
EffectiveReturningDate = loan.EffectiveReturningDate
}).ToList()
})
.ToListAsync(ct);
await Send.OkAsync(user, ct);
}
}

View File

@@ -0,0 +1,64 @@
using ApiEfCoreLibrary.DTO.Loan.Response;
using ApiEfCoreLibrary.DTO.User.Request;
using ApiEfCoreLibrary.DTO.User.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.User;
public class GetUserRequest
{
public int Id { get; set; }
}
public class GetUserEndpoint(LibraryDbContext database) : Endpoint<GetUserRequest, GetUserDto>
{
public override void Configure()
{
Get("/api/users/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(GetUserRequest req, CancellationToken ct)
{
var user = await database.Users
.Include(x => x.Loans)!
.ThenInclude(l => l.Book)
.ThenInclude(b => b!.Author)
.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (user == null)
{
await Send.NotFoundAsync(ct);
return;
}
GetUserDto responseDto = new()
{
Id = user.Id,
Name = user.Name,
FirstName = user.FirstName,
Email = user.Email,
BirthDate = user.BirthDate,
Loans = user.Loans?.Select(loan => new GetLoanDto
{
Id = loan.Id,
BookId = loan.BookId,
BookTitle = loan.Book.Title,
BookAuthorName = loan.Book.Author.Name,
BookAuthorFirstName = loan.Book.Author.FirstName,
BookReleaseYear = loan.Book.ReleaseYear,
BookIsbn = loan.Book.Isbn,
UserId = loan.UserId,
UserName = loan.User?.Name,
UserFirstName = loan.User?.FirstName,
UserEmail = loan.User?.Email,
Date = loan.Date,
PlannedReturningDate = loan.PlannedReturningDate,
EffectiveReturningDate = loan.EffectiveReturningDate
}).ToList()
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,43 @@
using ApiEfCoreLibrary.DTO.User.Request;
using ApiEfCoreLibrary.DTO.User.Response;
using FastEndpoints;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary.Endpoints.User;
public class UpdateUserEndpoint(LibraryDbContext database) : Endpoint<UpdateUserDto, GetUserDto>
{
public override void Configure()
{
Put("/api/users/{@Id}", x => new {x.Id});
AllowAnonymous();
}
public override async Task HandleAsync(UpdateUserDto req, CancellationToken ct)
{
var user = await database.Users.SingleOrDefaultAsync(x => x.Id == req.Id, ct);
if (user == null)
{
await Send.NotFoundAsync(ct);
return;
}
user.Name = req.Name;
user.FirstName = req.FirstName;
user.Email = req.Email;
user.BirthDate = req.BirthDate;
await database.SaveChangesAsync(ct);
GetUserDto responseDto = new()
{
Id = user.Id,
Name = user.Name,
FirstName = user.FirstName,
Email = user.Email,
BirthDate = user.BirthDate
};
await Send.OkAsync(responseDto, ct);
}
}

View File

@@ -0,0 +1,32 @@
using ApiEfCoreLibrary.Models;
using Microsoft.EntityFrameworkCore;
namespace ApiEfCoreLibrary;
public class LibraryDbContext : DbContext
{
// Tables représentées par les entités
public DbSet<Book> Books { get; set; } // Table des livres
public DbSet<Author> Authors { get; set; } // Table des auteurs
public DbSet<Loan> Loans { get; set; } // Table des prêts
public DbSet<User> Users { get; set; } // Table des utilisateurs
public DbSet<Login> Logins { get; set; } // Table des logins
// Configuration de la connexion à la base de données
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
string connectionString =
"Server=romaric-thibault.fr;" + // Serveur SQL
"Database=mathys_EfCoreLibrary;" + // Nom de la base
"User Id=mathys;" + // Utilisateur
"Password=Onto9-Cage-Afflicted;" + // Mot de passe
"TrustServerCertificate=true;"; // Accepte certificat auto-signé
optionsBuilder.UseSqlServer(connectionString);
}
// Personnalisation du modèle (non utilisée ici)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
}
}

View File

@@ -0,0 +1,187 @@
// <auto-generated />
using System;
using ApiEfCoreLibrary;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace ApiEfCoreLibrary.Migrations
{
[DbContext(typeof(LibraryDbContext))]
[Migration("20250923063940_InitialDatabase")]
partial class InitialDatabase
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.20")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("EfCoreLibrary.Models.Author", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.ToTable("Authors");
});
modelBuilder.Entity("EfCoreLibrary.Models.Book", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AuthorId")
.HasColumnType("int");
b.Property<string>("Isbn")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("nvarchar(20)");
b.Property<int?>("ReleaseYear")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.ToTable("Books");
});
modelBuilder.Entity("EfCoreLibrary.Models.Loan", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BookId")
.HasColumnType("int");
b.Property<DateOnly>("Date")
.HasColumnType("date");
b.Property<DateOnly?>("EffectiveReturningDate")
.HasColumnType("date");
b.Property<DateOnly>("PlannedReturningDate")
.HasColumnType("date");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("BookId");
b.HasIndex("UserId");
b.ToTable("Loans");
});
modelBuilder.Entity("EfCoreLibrary.Models.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateOnly?>("BirthDate")
.HasColumnType("date");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("EfCoreLibrary.Models.Book", b =>
{
b.HasOne("EfCoreLibrary.Models.Author", "Author")
.WithMany("Books")
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
});
modelBuilder.Entity("EfCoreLibrary.Models.Loan", b =>
{
b.HasOne("EfCoreLibrary.Models.Book", "Book")
.WithMany()
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("EfCoreLibrary.Models.User", "User")
.WithMany("Loans")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("User");
});
modelBuilder.Entity("EfCoreLibrary.Models.Author", b =>
{
b.Navigation("Books");
});
modelBuilder.Entity("EfCoreLibrary.Models.User", b =>
{
b.Navigation("Loans");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,127 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace ApiEfCoreLibrary.Migrations
{
/// <inheritdoc />
public partial class InitialDatabase : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Authors",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
FirstName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Authors", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Name = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
FirstName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
Email = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false),
BirthDate = table.Column<DateOnly>(type: "date", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Books",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
Title = table.Column<string>(type: "nvarchar(255)", maxLength: 255, nullable: false),
AuthorId = table.Column<int>(type: "int", nullable: false),
ReleaseYear = table.Column<int>(type: "int", nullable: true),
Isbn = table.Column<string>(type: "nvarchar(20)", maxLength: 20, nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Books", x => x.Id);
table.ForeignKey(
name: "FK_Books_Authors_AuthorId",
column: x => x.AuthorId,
principalTable: "Authors",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Loans",
columns: table => new
{
Id = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
BookId = table.Column<int>(type: "int", nullable: false),
UserId = table.Column<int>(type: "int", nullable: false),
Date = table.Column<DateOnly>(type: "date", nullable: false),
PlannedReturningDate = table.Column<DateOnly>(type: "date", nullable: false),
EffectiveReturningDate = table.Column<DateOnly>(type: "date", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Loans", x => x.Id);
table.ForeignKey(
name: "FK_Loans_Books_BookId",
column: x => x.BookId,
principalTable: "Books",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Loans_Users_UserId",
column: x => x.UserId,
principalTable: "Users",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Books_AuthorId",
table: "Books",
column: "AuthorId");
migrationBuilder.CreateIndex(
name: "IX_Loans_BookId",
table: "Loans",
column: "BookId");
migrationBuilder.CreateIndex(
name: "IX_Loans_UserId",
table: "Loans",
column: "UserId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Loans");
migrationBuilder.DropTable(
name: "Books");
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Authors");
}
}
}

View File

@@ -0,0 +1,217 @@
// <auto-generated />
using System;
using ApiEfCoreLibrary;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace ApiEfCoreLibrary.Migrations
{
[DbContext(typeof(LibraryDbContext))]
partial class LibraryDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.20")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("ApiEfCoreLibrary.Models.Author", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.ToTable("Authors");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.Book", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("AuthorId")
.HasColumnType("int");
b.Property<string>("Isbn")
.IsRequired()
.HasMaxLength(20)
.HasColumnType("nvarchar(20)");
b.Property<int?>("ReleaseYear")
.HasColumnType("int");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.HasKey("Id");
b.HasIndex("AuthorId");
b.ToTable("Books");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.Loan", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<int>("BookId")
.HasColumnType("int");
b.Property<DateOnly>("Date")
.HasColumnType("date");
b.Property<DateOnly?>("EffectiveReturningDate")
.HasColumnType("date");
b.Property<DateOnly>("PlannedReturningDate")
.HasColumnType("date");
b.Property<int>("UserId")
.HasColumnType("int");
b.HasKey("Id");
b.HasIndex("BookId");
b.HasIndex("UserId");
b.ToTable("Loans");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.Login", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("FullName")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("nvarchar(200)");
b.Property<string>("Password")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("Salt")
.IsRequired()
.HasMaxLength(24)
.HasColumnType("nvarchar(24)");
b.Property<string>("Username")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.ToTable("Logins");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateOnly?>("BirthDate")
.HasColumnType("date");
b.Property<string>("Email")
.IsRequired()
.HasMaxLength(255)
.HasColumnType("nvarchar(255)");
b.Property<string>("FirstName")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("nvarchar(100)");
b.HasKey("Id");
b.ToTable("Users");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.Book", b =>
{
b.HasOne("ApiEfCoreLibrary.Models.Author", "Author")
.WithMany("Books")
.HasForeignKey("AuthorId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Author");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.Loan", b =>
{
b.HasOne("ApiEfCoreLibrary.Models.Book", "Book")
.WithMany()
.HasForeignKey("BookId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("ApiEfCoreLibrary.Models.User", "User")
.WithMany("Loans")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Book");
b.Navigation("User");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.Author", b =>
{
b.Navigation("Books");
});
modelBuilder.Entity("ApiEfCoreLibrary.Models.User", b =>
{
b.Navigation("Loans");
});
#pragma warning restore 612, 618
}
}
}

View File

@@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace ApiEfCoreLibrary.Models;
public class Author
{
[Key] public int Id { get; set; }
[Required, MaxLength(100)] public string? Name { get; set; }
[Required, MaxLength(100)] public string? FirstName { get; set; }
public List<Book>? Books { get; set; }
}

View File

@@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace ApiEfCoreLibrary.Models;
public class Book
{
[Key] public int Id { get; set; }
[Required, MaxLength(255)] public string? Title { get; set; }
[Required] public int AuthorId { get; set; }
public int? ReleaseYear { get; set; }
[Required, MaxLength(20)] public string? Isbn { get; set; }
public Author? Author { get; set; }
}

View File

@@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
namespace ApiEfCoreLibrary.Models;
public class Loan
{
[Key] public int Id { get; set; }
[Required] public int BookId { get; set; }
[Required] public int UserId { get; set; }
[Required] public DateOnly Date { get; set; }
[Required] public DateOnly PlannedReturningDate { get; set; }
public DateOnly? EffectiveReturningDate { get; set; }
public Book? Book { get; set; }
public User? User { get; set; }
}

View File

@@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;
namespace ApiEfCoreLibrary.Models;
public class Login
{
[Key] public int Id { get; set; }
[Required, MaxLength(100)] public string? Username { get; set; }
[Required, MaxLength(200)] public string? FullName { get; set; }
[Required, MaxLength(255)] public string? Password { get; set; }
[Required, MaxLength(24)] public string? Salt { get; set; }
}

View File

@@ -0,0 +1,13 @@
using System.ComponentModel.DataAnnotations;
namespace ApiEfCoreLibrary.Models;
public class User
{
[Key] public int Id { get; set; }
[Required, MaxLength(100)] public string? Name { get; set; }
[Required, MaxLength(100)] public string? FirstName { get; set; }
[Required, MaxLength(255)] public string? Email { get; set; }
public DateOnly? BirthDate { get; set; }
public List<Loan>? Loans { get; set; }
}

View File

@@ -0,0 +1,27 @@
using ApiEfCoreLibrary;
using FastEndpoints;
using FastEndpoints.Swagger;
using FastEndpoints.Security;
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// On ajoute ici FastEndpoints, un framework REPR et Swagger aux services disponibles dans le projet
builder.Services
.AddAuthenticationJwtBearer(s => s.SigningKey = "The secret used to sign tokens")
.AddAuthorization()
.AddFastEndpoints()
.SwaggerDocument();
// On ajoute ici la configuration de la base de données
builder.Services.AddDbContext<LibraryDbContext>();
// On construit l'application en lui donnant vie
WebApplication app = builder.Build();
app.UseAuthentication()
.UseAuthorization()
.UseFastEndpoints()
.UseSwaggerGen();
app.UseHttpsRedirection();
app.Run();

View File

@@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:49674",
"sslPort": 44390
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5228",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7214;http://localhost:5228",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}