From 587d1b70127a31641e1e0b76c8a1e7ced773042e Mon Sep 17 00:00:00 2001 From: Rodrigo Cour Date: Sat, 12 Mar 2022 15:13:12 -0300 Subject: [PATCH] Workshop - All unit tests passes --- ...20220312180836_AddMissingItems.Designer.cs | 175 ++++++++++++++++++ .../20220312180836_AddMissingItems.cs | 102 ++++++++++ .../Migrations/ApiContextModelSnapshot.cs | 67 +++++++ .../Services/ApiContext.cs | 48 ++++- .../Services/EfRepository.cs | 122 +++++++----- 5 files changed, 462 insertions(+), 52 deletions(-) create mode 100644 EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.Designer.cs create mode 100644 EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.cs diff --git a/EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.Designer.cs b/EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.Designer.cs new file mode 100644 index 0000000..5d5f5f2 --- /dev/null +++ b/EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.Designer.cs @@ -0,0 +1,175 @@ +// +using EF.Core.Training; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace EF.Core.Training.Migrations +{ + [DbContext(typeof(ApiContext))] + [Migration("20220312180836_AddMissingItems")] + partial class AddMissingItems + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.1"); + + modelBuilder.Entity("EF.Core.Training.BlackBox.Author", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Bio") + .HasColumnType("TEXT"); + + b.Property("First") + .HasColumnType("TEXT"); + + b.Property("Last") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.ToTable("Author", (string)null); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.AuthorBookLink", b => + { + b.Property("AuthorID") + .HasColumnType("INTEGER"); + + b.Property("BookID") + .HasColumnType("INTEGER"); + + b.HasKey("AuthorID", "BookID"); + + b.HasIndex("BookID"); + + b.ToTable("AuthorBookLink", (string)null); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.Book", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasColumnType("TEXT"); + + b.Property("ISBN") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Pages") + .HasColumnType("INTERGER"); + + b.Property("Price") + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.ToTable("Book", (string)null); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.BookGenreLink", b => + { + b.Property("BookID") + .HasColumnType("INTEGER"); + + b.Property("GenreID") + .HasColumnType("INTEGER"); + + b.HasKey("BookID", "GenreID"); + + b.HasIndex("GenreID"); + + b.ToTable("BookGenreLink", (string)null); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.Genre", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.ToTable("Genre", (string)null); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.AuthorBookLink", b => + { + b.HasOne("EF.Core.Training.BlackBox.Author", "Author") + .WithMany("BookLinks") + .HasForeignKey("AuthorID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EF.Core.Training.BlackBox.Book", "Book") + .WithMany("AuthorLinks") + .HasForeignKey("BookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Author"); + + b.Navigation("Book"); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.BookGenreLink", b => + { + b.HasOne("EF.Core.Training.BlackBox.Book", "Book") + .WithMany("GenreLinks") + .HasForeignKey("BookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EF.Core.Training.BlackBox.Genre", "Genre") + .WithMany("BookLinks") + .HasForeignKey("GenreID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Book"); + + b.Navigation("Genre"); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.Author", b => + { + b.Navigation("BookLinks"); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.Book", b => + { + b.Navigation("AuthorLinks"); + + b.Navigation("GenreLinks"); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.Genre", b => + { + b.Navigation("BookLinks"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.cs b/EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.cs new file mode 100644 index 0000000..8b2ab8a --- /dev/null +++ b/EF.Core.Training.CodeShop/Migrations/20220312180836_AddMissingItems.cs @@ -0,0 +1,102 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EF.Core.Training.Migrations +{ + public partial class AddMissingItems : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "Title", + table: "Book", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "ISBN", + table: "Book", + type: "TEXT", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "TEXT", + oldNullable: true); + + migrationBuilder.CreateTable( + name: "Author", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: false), + First = table.Column(type: "TEXT", nullable: true), + Last = table.Column(type: "TEXT", nullable: true), + Bio = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Author", x => x.ID); + }); + + migrationBuilder.CreateTable( + name: "AuthorBookLink", + columns: table => new + { + AuthorID = table.Column(type: "INTEGER", nullable: false), + BookID = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AuthorBookLink", x => new { x.AuthorID, x.BookID }); + table.ForeignKey( + name: "FK_AuthorBookLink_Author_AuthorID", + column: x => x.AuthorID, + principalTable: "Author", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AuthorBookLink_Book_BookID", + column: x => x.BookID, + principalTable: "Book", + principalColumn: "ID", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_AuthorBookLink_BookID", + table: "AuthorBookLink", + column: "BookID"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AuthorBookLink"); + + migrationBuilder.DropTable( + name: "Author"); + + migrationBuilder.AlterColumn( + name: "Title", + table: "Book", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + + migrationBuilder.AlterColumn( + name: "ISBN", + table: "Book", + type: "TEXT", + nullable: true, + oldClrType: typeof(string), + oldType: "TEXT"); + } + } +} diff --git a/EF.Core.Training.CodeShop/Migrations/ApiContextModelSnapshot.cs b/EF.Core.Training.CodeShop/Migrations/ApiContextModelSnapshot.cs index 68c6d37..4aa52f3 100644 --- a/EF.Core.Training.CodeShop/Migrations/ApiContextModelSnapshot.cs +++ b/EF.Core.Training.CodeShop/Migrations/ApiContextModelSnapshot.cs @@ -16,6 +16,45 @@ protected override void BuildModel(ModelBuilder modelBuilder) #pragma warning disable 612, 618 modelBuilder.HasAnnotation("ProductVersion", "6.0.1"); + modelBuilder.Entity("EF.Core.Training.BlackBox.Author", b => + { + b.Property("ID") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Bio") + .HasColumnType("TEXT"); + + b.Property("First") + .HasColumnType("TEXT"); + + b.Property("Last") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("ID"); + + b.ToTable("Author", (string)null); + }); + + modelBuilder.Entity("EF.Core.Training.BlackBox.AuthorBookLink", b => + { + b.Property("AuthorID") + .HasColumnType("INTEGER"); + + b.Property("BookID") + .HasColumnType("INTEGER"); + + b.HasKey("AuthorID", "BookID"); + + b.HasIndex("BookID"); + + b.ToTable("AuthorBookLink", (string)null); + }); + modelBuilder.Entity("EF.Core.Training.BlackBox.Book", b => { b.Property("ID") @@ -26,6 +65,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("ISBN") + .IsRequired() .HasColumnType("TEXT"); b.Property("Pages") @@ -35,6 +75,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("TEXT"); b.Property("Title") + .IsRequired() .HasColumnType("TEXT"); b.HasKey("ID"); @@ -72,6 +113,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("Genre", (string)null); }); + modelBuilder.Entity("EF.Core.Training.BlackBox.AuthorBookLink", b => + { + b.HasOne("EF.Core.Training.BlackBox.Author", "Author") + .WithMany("BookLinks") + .HasForeignKey("AuthorID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("EF.Core.Training.BlackBox.Book", "Book") + .WithMany("AuthorLinks") + .HasForeignKey("BookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Author"); + + b.Navigation("Book"); + }); + modelBuilder.Entity("EF.Core.Training.BlackBox.BookGenreLink", b => { b.HasOne("EF.Core.Training.BlackBox.Book", "Book") @@ -91,8 +151,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Genre"); }); + modelBuilder.Entity("EF.Core.Training.BlackBox.Author", b => + { + b.Navigation("BookLinks"); + }); + modelBuilder.Entity("EF.Core.Training.BlackBox.Book", b => { + b.Navigation("AuthorLinks"); + b.Navigation("GenreLinks"); }); diff --git a/EF.Core.Training.CodeShop/Services/ApiContext.cs b/EF.Core.Training.CodeShop/Services/ApiContext.cs index af1ffd5..2f64ed2 100644 --- a/EF.Core.Training.CodeShop/Services/ApiContext.cs +++ b/EF.Core.Training.CodeShop/Services/ApiContext.cs @@ -18,6 +18,8 @@ public ApiContext() // ALL CODE CHANGES SHOULD HAPPEN BELOW THIS COMMENT + public DbSet Authors { get; set; } + public DbSet AuthorBookLinks { get; set; } public DbSet Books { get; set; } public DbSet Genres { get; set; } public DbSet BookGenreLinks { get; set; } @@ -57,16 +59,52 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasKey("ID"); // something might be missing on these .. - entity.Property(e => e.ISBN).HasColumnType("TEXT"); - entity.Property(e => e.Title).HasColumnType("TEXT"); + entity.Property(e => e.ISBN).HasColumnType("TEXT").IsRequired(); + entity.Property(e => e.Title).HasColumnType("TEXT").IsRequired(); entity.Property(e => e.Description).HasColumnType("TEXT"); entity.Property(e => e.Price).HasColumnType("TEXT").IsRequired(); entity.Property(e => e.Pages).HasColumnType("INTERGER").IsRequired(); // something is wrong about these .. - entity.HasMany(e => e.GenreLinks).WithOne(l => l.Book) - .HasForeignKey(l => l.BookID).OnDelete(DeleteBehavior.Cascade); - entity.Ignore(e => e.AuthorLinks); + entity.HasMany(e => e.GenreLinks) + .WithOne(l => l.Book) + .HasForeignKey(l => l.BookID) + .OnDelete(DeleteBehavior.Cascade); + + entity.HasMany(e => e.AuthorLinks) + .WithOne(l => l.Book) + .HasForeignKey(l => l.BookID) + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("Author"); + entity.HasKey("ID"); + + entity.Property(e => e.Name).HasColumnType("TEXT").IsRequired(); + entity.Property(e => e.First).HasColumnType("TEXT"); + entity.Property(e => e.Last).HasColumnType("TEXT"); + entity.Property(e => e.Bio).HasColumnType("TEXT"); + + entity.HasMany(e => e.BookLinks) + .WithOne(l => l.Author) + .HasForeignKey(l => l.AuthorID) + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("AuthorBookLink"); + entity.HasKey(e => new { e.AuthorID, e.BookID }); + + entity.HasOne(e => e.Author) + .WithMany(x => x.BookLinks) + .HasForeignKey(e => e.AuthorID); + + entity.HasOne(e => e.Book) + .WithMany(x => x.AuthorLinks) + .HasForeignKey(e => e.BookID); }); // TODO : Add the other two modelBuilder.Entity setups diff --git a/EF.Core.Training.CodeShop/Services/EfRepository.cs b/EF.Core.Training.CodeShop/Services/EfRepository.cs index 20d1eb4..fc01248 100644 --- a/EF.Core.Training.CodeShop/Services/EfRepository.cs +++ b/EF.Core.Training.CodeShop/Services/EfRepository.cs @@ -23,14 +23,11 @@ public async Task MigrateDb() // ALL CODE CHANGES SHOULD HAPPEN BELOW THIS COMMENT - public Task CreateAuthor(Author author) + public async Task CreateAuthor(Author author) { - throw new NotImplementedException(); - } - - public Task CreateAuthorBookLink(AuthorBookLink link) - { - throw new NotImplementedException(); + apiContext.Authors.Add(author); + await apiContext.SaveChangesAsync(); + return author; } public async Task CreateBook(Book book) @@ -40,38 +37,65 @@ public async Task CreateBook(Book book) return book; } - public async Task CreateBookGenreLink(BookGenreLink link) + public async Task CreateGenre(Genre genre) { - apiContext.BookGenreLinks.Add(link); + apiContext.Genres.Add(genre); + await apiContext.SaveChangesAsync(); + return genre; + } + + public async Task CreateAuthorBookLink(AuthorBookLink link) + { + apiContext.AuthorBookLinks.Add(link); await apiContext.SaveChangesAsync(); return link; } - public async Task CreateGenre(Genre genre) + public async Task CreateBookGenreLink(BookGenreLink link) { - apiContext.Genres.Add(genre); + apiContext.BookGenreLinks.Add(link); await apiContext.SaveChangesAsync(); - return genre; + return link; } - public Task DeleteAuthor(Author author) + public async Task DeleteAuthor(Author author) { - throw new NotImplementedException(); + try + { + await author.DoBeforeDelete(this); + apiContext.Authors.Remove(author); + return await apiContext.SaveChangesAsync() > 0; + } + catch + { + return false; + } } - public Task DeleteAuthorBookLink(AuthorBookLink link) + public async Task DeleteAuthorBookLink(AuthorBookLink link) { - throw new NotImplementedException(); + apiContext.Remove(link); + return await apiContext.SaveChangesAsync() > 0; } - public Task DeleteAuthorBookLinksForBook(int bookID) + public async Task DeleteAuthorBookLinksForBook(int bookID) { - throw new NotImplementedException(); + apiContext.RemoveRange(apiContext.AuthorBookLinks.Where(x => x.BookID == bookID)); + return await apiContext.SaveChangesAsync() > 0; } - public Task DeleteBook(Book book) + public async Task DeleteBook(Book book) { - throw new NotImplementedException(); + try + { + await book.DoBeforeDelete(this); + apiContext.Books.Remove(book); + return await apiContext.SaveChangesAsync() > 0; + } + catch + { + return false; + } } public async Task DeleteBookGenreLink(BookGenreLink link) @@ -99,35 +123,34 @@ public async Task DeleteGenre(Genre genre) return false; } } - - public Task> RetrieveAuthorBookLinksByAuthorID(int authorID) + public async Task> RetrieveAuthorBookLinksByAuthorID(int authorID) { - throw new NotImplementedException(); + return await apiContext.AuthorBookLinks.Where(x => x.AuthorID == authorID).ToListAsync(); } - public Task> RetrieveAuthorBookLinksByBookID(int bookID) + public async Task> RetrieveAuthorBookLinksByBookID(int bookID) { - throw new NotImplementedException(); + return await apiContext.AuthorBookLinks.Where(x => x.BookID == bookID).ToListAsync(); } - public Task RetrieveAuthorByID(int authorID) + public async Task RetrieveAuthorByID(int authorID) { - throw new NotImplementedException(); + return await apiContext.Authors.FirstOrDefaultAsync(x => x.ID == authorID); } - public Task> RetrieveAuthors() + public async Task> RetrieveAuthors() { - throw new NotImplementedException(); + return await apiContext.Authors.ToListAsync(); } - public Task> RetrieveAuthorsByBookID(int bookID) + public async Task> RetrieveAuthorsByBookID(int bookID) { - throw new NotImplementedException(); + return await apiContext.AuthorBookLinks.Where(a => a.BookID == bookID).Distinct().Select(x => x.Author).ToListAsync(); } - public Task RetrieveBookByID(int bookID) + public async Task RetrieveBookByID(int bookID) { - throw new NotImplementedException(); + return await apiContext.Books.FirstOrDefaultAsync(x => x.ID == bookID); } public async Task> RetrieveBookGenreLinksByBookID(int bookID) @@ -135,19 +158,19 @@ public async Task> RetrieveBookGenreLinksByBookID(int return await apiContext.BookGenreLinks.Where(x => x.BookID == bookID).ToListAsync(); } - public Task> RetrieveBooks() + public async Task> RetrieveBooks() { - throw new NotImplementedException(); + return await apiContext.Books.ToListAsync(); } - public Task> RetrieveBooksByAuthorID(int authorID) + public async Task> RetrieveBooksByAuthorID(int authorID) { - throw new NotImplementedException(); + return await apiContext.AuthorBookLinks.Where(a => a.AuthorID == authorID).Distinct().Select(x => x.Book).ToListAsync(); } - public Task> RetrieveBooksByGenreID(int genreID) + public async Task> RetrieveBooksByGenreID(int genreID) { - throw new NotImplementedException(); + return await apiContext.BookGenreLinks.Where(a => a.GenreID == genreID).Distinct().Select(a => a.Book).ToListAsync(); } public async Task RetrieveGenreByID(int genreID) @@ -162,18 +185,23 @@ public async Task> RetrieveGenres() public async Task> RetrieveGenresByBookID(int bookID) { - return await apiContext.Genres.Include(x => x.BookLinks) - .Where(x => x.BookLinks.Any(l => l.BookID == bookID)).ToListAsync(); + return await apiContext.BookGenreLinks.Where(a => a.BookID == bookID).Distinct().Select(a => a.Genre).ToListAsync(); } - - public Task UpdateAuthor(Author author) + + public async Task UpdateAuthor(Author author) { - throw new NotImplementedException(); + apiContext.Authors.Update(author); + await apiContext.SaveChangesAsync(); + + return author; } - public Task UpdateBook(Book book) + public async Task UpdateBook(Book book) { - throw new NotImplementedException(); + apiContext.Books.Update(book); + await apiContext.SaveChangesAsync(); + + return book; } public async Task UpdateGenre(Genre genre) @@ -184,4 +212,4 @@ public async Task UpdateGenre(Genre genre) return genre; } } -} +} \ No newline at end of file