From 55869e19d3733bb7f4b350f811462610cfa31530 Mon Sep 17 00:00:00 2001 From: stp Date: Mon, 27 Jan 2025 15:26:13 +0100 Subject: [PATCH] Steven Phung --- exercise.pizzashopapi/DTO/CustomerDTO.cs | 8 + exercise.pizzashopapi/DTO/CustomerOrderDTO.cs | 9 + exercise.pizzashopapi/DTO/CustomerPut.cs | 7 + exercise.pizzashopapi/DTO/OrderCustomerDTO.cs | 7 + exercise.pizzashopapi/DTO/OrderDTO.cs | 10 + exercise.pizzashopapi/DTO/PizzaDTO.cs | 9 + exercise.pizzashopapi/DTO/PizzaListDTO.cs | 9 + exercise.pizzashopapi/Data/DataContext.cs | 3 +- exercise.pizzashopapi/Data/Seeder.cs | 15 +- .../EndPoints/PizzaShopApi.cs | 226 +++++++++++++++++- exercise.pizzashopapi/Mapper/Mapping.cs | 42 ++++ .../20250127095622_InitialCreate.Designer.cs | 124 ++++++++++ .../20250127095622_InitialCreate.cs | 91 +++++++ .../Migrations/DataContextModelSnapshot.cs | 121 ++++++++++ exercise.pizzashopapi/Models/Customer.cs | 10 +- exercise.pizzashopapi/Models/Order.cs | 27 ++- exercise.pizzashopapi/Models/Pizza.cs | 14 +- exercise.pizzashopapi/Program.cs | 11 +- .../Repository/IRepository.cs | 20 +- .../Repository/Repository.cs | 89 ++++++- .../exercise.pizzashopapi.csproj | 19 +- 21 files changed, 843 insertions(+), 28 deletions(-) create mode 100644 exercise.pizzashopapi/DTO/CustomerDTO.cs create mode 100644 exercise.pizzashopapi/DTO/CustomerOrderDTO.cs create mode 100644 exercise.pizzashopapi/DTO/CustomerPut.cs create mode 100644 exercise.pizzashopapi/DTO/OrderCustomerDTO.cs create mode 100644 exercise.pizzashopapi/DTO/OrderDTO.cs create mode 100644 exercise.pizzashopapi/DTO/PizzaDTO.cs create mode 100644 exercise.pizzashopapi/DTO/PizzaListDTO.cs create mode 100644 exercise.pizzashopapi/Mapper/Mapping.cs create mode 100644 exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.Designer.cs create mode 100644 exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.cs create mode 100644 exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs diff --git a/exercise.pizzashopapi/DTO/CustomerDTO.cs b/exercise.pizzashopapi/DTO/CustomerDTO.cs new file mode 100644 index 0000000..f01e6a9 --- /dev/null +++ b/exercise.pizzashopapi/DTO/CustomerDTO.cs @@ -0,0 +1,8 @@ +namespace exercise.pizzashopapi.DTO +{ + public class CustomerDTO + { + public string Name { get; set; } + public List Orders { get; set; } = new List(); + } +} diff --git a/exercise.pizzashopapi/DTO/CustomerOrderDTO.cs b/exercise.pizzashopapi/DTO/CustomerOrderDTO.cs new file mode 100644 index 0000000..e973576 --- /dev/null +++ b/exercise.pizzashopapi/DTO/CustomerOrderDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.pizzashopapi.DTO +{ + public class CustomerOrderDTO + { + public int OrderId { get; set; } + public PizzaDTO Pizza { get; set; } + } + +} diff --git a/exercise.pizzashopapi/DTO/CustomerPut.cs b/exercise.pizzashopapi/DTO/CustomerPut.cs new file mode 100644 index 0000000..5e4aec0 --- /dev/null +++ b/exercise.pizzashopapi/DTO/CustomerPut.cs @@ -0,0 +1,7 @@ +namespace exercise.pizzashopapi.DTO +{ + public class CustomerPut + { + public string Name { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/OrderCustomerDTO.cs b/exercise.pizzashopapi/DTO/OrderCustomerDTO.cs new file mode 100644 index 0000000..660e577 --- /dev/null +++ b/exercise.pizzashopapi/DTO/OrderCustomerDTO.cs @@ -0,0 +1,7 @@ +namespace exercise.pizzashopapi.DTO +{ + public class OrderCustomerDTO + { + public string Name { get; set; } + } +} diff --git a/exercise.pizzashopapi/DTO/OrderDTO.cs b/exercise.pizzashopapi/DTO/OrderDTO.cs new file mode 100644 index 0000000..173964e --- /dev/null +++ b/exercise.pizzashopapi/DTO/OrderDTO.cs @@ -0,0 +1,10 @@ +namespace exercise.pizzashopapi.DTO +{ + public class OrderDTO + { + public int Id { get; set; } + public OrderCustomerDTO Customer { get; set; } + public PizzaDTO Pizza { get; set; } + + } +} diff --git a/exercise.pizzashopapi/DTO/PizzaDTO.cs b/exercise.pizzashopapi/DTO/PizzaDTO.cs new file mode 100644 index 0000000..c5de813 --- /dev/null +++ b/exercise.pizzashopapi/DTO/PizzaDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.pizzashopapi.DTO +{ + public class PizzaDTO + { + public string Name { get; set; } + public decimal Price { get; set; } + //public List OrderPizza { get; set; } = new List(); + } +} diff --git a/exercise.pizzashopapi/DTO/PizzaListDTO.cs b/exercise.pizzashopapi/DTO/PizzaListDTO.cs new file mode 100644 index 0000000..c0f7e6b --- /dev/null +++ b/exercise.pizzashopapi/DTO/PizzaListDTO.cs @@ -0,0 +1,9 @@ +namespace exercise.pizzashopapi.DTO +{ + public class PizzaListDTO + { + public int Id { get; set; } + public string Name { get; set; } + public decimal Price { get; set; } + } +} diff --git a/exercise.pizzashopapi/Data/DataContext.cs b/exercise.pizzashopapi/Data/DataContext.cs index 129199e..0bfb7b9 100644 --- a/exercise.pizzashopapi/Data/DataContext.cs +++ b/exercise.pizzashopapi/Data/DataContext.cs @@ -12,11 +12,12 @@ public DataContext() connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString"); } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(connectionString); - //set primary of order? + //set primary of order //seed data? diff --git a/exercise.pizzashopapi/Data/Seeder.cs b/exercise.pizzashopapi/Data/Seeder.cs index f87fbef..d13de5e 100644 --- a/exercise.pizzashopapi/Data/Seeder.cs +++ b/exercise.pizzashopapi/Data/Seeder.cs @@ -12,15 +12,26 @@ public async static void SeedPizzaShopApi(this WebApplication app) { db.Add(new Customer() { Name="Nigel" }); db.Add(new Customer() { Name = "Dave" }); + db.Add(new Customer() { Name = "Steven" }); + await db.SaveChangesAsync(); } if(!db.Pizzas.Any()) { - db.Add(new Pizza() { Name = "Cheese & Pineapple" }); - db.Add(new Pizza() { Name = "Vegan Cheese Tastic" }); + db.Add(new Pizza() { Name = "Cheese & Pineapple", Price = 139.99M }); + db.Add(new Pizza() { Name = "Vegan Cheese Tastic" , Price = 119.99M}); + db.Add(new Pizza() { Name = "Marshmellow", Price = 89.99M }); + await db.SaveChangesAsync(); } + if (!db.Orders.Any()) + { + db.Add(new Order() { CustomerId = 1, PizzaId = 2 }); + db.Add(new Order() { CustomerId = 2, PizzaId = 1 }); + db.Add(new Order() { CustomerId = 3, PizzaId = 3 }); + await db.SaveChangesAsync(); + } //order data if(1==1) diff --git a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs index f8be2b0..c4baf0d 100644 --- a/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs +++ b/exercise.pizzashopapi/EndPoints/PizzaShopApi.cs @@ -1,5 +1,10 @@ -using exercise.pizzashopapi.Repository; +using AutoMapper; +using exercise.pizzashopapi.DTO; +using exercise.pizzashopapi.Models; +using exercise.pizzashopapi.Repository; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.EntityFrameworkCore; namespace exercise.pizzashopapi.EndPoints { @@ -7,9 +12,224 @@ public static class PizzaShopApi { public static void ConfigurePizzaShopApi(this WebApplication app) { - + + var PizzaGroup = app.MapGroup("/pizza"); + + PizzaGroup.MapGet("/", GetPizzas); + PizzaGroup.MapGet("/{id}", GetPizza); + PizzaGroup.MapPost("/", AddPizza); + PizzaGroup.MapPut("/{id}", UpdatePizza); + PizzaGroup.MapDelete("/{id}", DeletePizza); + + var CustomerGroup = app.MapGroup("/customer"); + CustomerGroup.MapGet("/", GetCustomers); + CustomerGroup.MapGet("/{id}", GetCustomer); + CustomerGroup.MapPost("/", AddCustomer); + CustomerGroup.MapPut("/{id}", UpdateCustomer); + + var OrderGroup = app.MapGroup("/order"); + OrderGroup.MapGet("/", GetOrders); + OrderGroup.MapGet("/{id}", GetOrder); + OrderGroup.MapPost("/{customerId}/{pizzaId}", AddOrder); + + } + + #region Pizza + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetPizzas(IRepository repo, IMapper mapper ) + { + var pizzas = await repo.Get(); + var pizzaDTOs = mapper.Map>(pizzas); + return Results.Ok(pizzaDTOs); + + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetPizza(IRepository repo, int id, IMapper mapper) + { + var pizza = await repo.GetById(id); + if (pizza == null) + { + return Results.NotFound(); + } + + return Results.Ok(mapper.Map(pizza)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task AddPizza(IRepository repo, PizzaDTO pizza, IMapper mapper) + { + Pizza newPizza = new Pizza + { + Name = pizza.Name, + Price = pizza.Price + }; + + var result = await repo.Add(newPizza); + return Results.Ok(mapper.Map(result)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task UpdatePizza(IRepository repo, int id, PizzaDTO pizza, IMapper mapper) + { + var existingPizza = await repo.GetById(id); + if (existingPizza == null) + { + return Results.NotFound(); + } + existingPizza.Name = pizza.Name; + existingPizza.Price = pizza.Price; + var result = await repo.Update(existingPizza); + return TypedResults.Ok(mapper.Map(result)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task DeletePizza(IRepository repo, int id) + { + var existingPizza = await repo.GetById(id); + if (existingPizza == null) + { + return Results.NotFound(); + } + var result = await repo.Delete(id); + return TypedResults.Ok(result); + } + + #endregion + + #region Customer + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetCustomers(IRepository repo, IMapper mapper) + { + var customers = await repo + .GetWithNestedIncludes( query => query.Include(c => c.Orders) + .ThenInclude(o => o.Pizza)); + + return Results.Ok(mapper.Map>(customers)); + } - + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetCustomer(IRepository repo, int id, IMapper mapper) + { + var customer = await repo.GetQuery() + .Include(c => c.Orders) + .ThenInclude(o => o.Pizza) + .FirstOrDefaultAsync(c => c.Id == id); + if (customer == null) + { + return Results.NotFound(); + } + return Results.Ok(mapper.Map(customer)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task AddCustomer(IRepository repo, CustomerPut customer, IMapper mapper) + { + Customer newCustomer = new Customer + { + Name = customer.Name + }; + var result = await repo.Add(newCustomer); + return Results.Ok(mapper.Map(result)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task UpdateCustomer(IRepository repo, int id, CustomerPut customer, IMapper mapper) + { + var existingCustomer = await repo.GetById(id); + if (existingCustomer == null) + { + return Results.NotFound(); + } + existingCustomer.Name = customer.Name; + var result = await repo.Update(existingCustomer); + return TypedResults.Ok(mapper.Map(result)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task DeleteCustomer(IRepository repo, int id) + { + var existingCustomer = await repo.GetById(id); + if (existingCustomer == null) + { + return Results.NotFound(); + } + var result = await repo.Delete(id); + return TypedResults.Ok(result); + } + + #endregion + + #region Orders + + [ProducesResponseType(StatusCodes.Status200OK)] + public static async Task GetOrders(IRepository repo, IMapper mapper) + { + var orders = await repo.GetQuery() + .Include(o => o.Customer) + .Include(o => o.Pizza) + .ToListAsync(); + return Results.Ok(mapper.Map>(orders)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task GetOrder(IRepository repo, int id, IMapper mapper) + { + var order = await repo.GetQuery() + .Include(o => o.Customer) + .Include(o => o.Pizza) + .FirstOrDefaultAsync(o => o.Id == id); + if (order == null) + { + return Results.NotFound(); + } + return Results.Ok(mapper.Map(order)); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public static async Task AddOrder(IRepository orderRepo, IRepository pizzaRepo, IRepository customerRepo, int customerId, int pizzaId, IMapper mapper) + { + //var customer = await repo.GetById(customerId); + var customer = await customerRepo.GetQuery() + .FirstOrDefaultAsync(c => c.Id == customerId); + + if (customer == null) + { + return Results.NotFound(); + } + //var pizza = await repo.GetById(pizzaId); + var pizza = await pizzaRepo.GetQuery() + .FirstOrDefaultAsync(c => c.Id == pizzaId); + + if (pizza == null) + { + return Results.NotFound(); + } + + Order newOrder = new Order + { + CustomerId = customerId, + PizzaId = pizzaId, + Pizza = pizza, + Customer = customer, + + }; + var result = await orderRepo.Add(newOrder); + return Results.Ok(mapper.Map(result)); + } + + + #endregion } } diff --git a/exercise.pizzashopapi/Mapper/Mapping.cs b/exercise.pizzashopapi/Mapper/Mapping.cs new file mode 100644 index 0000000..881272f --- /dev/null +++ b/exercise.pizzashopapi/Mapper/Mapping.cs @@ -0,0 +1,42 @@ +using AutoMapper; +using exercise.pizzashopapi.DTO; +using exercise.pizzashopapi.Models; +namespace exercise.pizzashopapi.Mapper +{ + public class Mapping : Profile + { + + public Mapping() + { + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + + CreateMap() + .ForMember(dest => dest.Orders, opt => opt.MapFrom(src => src.Orders)); + + CreateMap() + .ForMember(dest => dest.Pizza, opt => opt.MapFrom(src => src.Pizza)) + .ForMember(dest => dest.OrderId, opt => opt.MapFrom(src => src.Id)); + + CreateMap() + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Customer.Name)); + + CreateMap() + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Pizza)); + + CreateMap() + .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer)) + .ForMember(dest => dest.Pizza, opt => opt.MapFrom(src => src.Pizza)); + + CreateMap() + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)); + + CreateMap() + .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name)) + .ForMember(dest => dest.Price, opt => opt.MapFrom(src => src.Price)); + + } + } +} diff --git a/exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.Designer.cs b/exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.Designer.cs new file mode 100644 index 0000000..855fd17 --- /dev/null +++ b/exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.Designer.cs @@ -0,0 +1,124 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using exercise.pizzashopapi.Data; + +#nullable disable + +namespace exercise.pizzashopapi.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20250127095622_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("customers"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CustomerId") + .HasColumnType("integer") + .HasColumnName("customer_id"); + + b.Property("PizzaId") + .HasColumnType("integer") + .HasColumnName("pizza_id"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("PizzaId"); + + b.ToTable("orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Price") + .HasColumnType("numeric") + .HasColumnName("price"); + + b.HasKey("Id"); + + b.ToTable("pizzas"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.HasOne("exercise.pizzashopapi.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("exercise.pizzashopapi.Models.Pizza", "Pizza") + .WithMany("Orders") + .HasForeignKey("PizzaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Pizza"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Navigation("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.cs b/exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.cs new file mode 100644 index 0000000..f10da1a --- /dev/null +++ b/exercise.pizzashopapi/Migrations/20250127095622_InitialCreate.cs @@ -0,0 +1,91 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace exercise.pizzashopapi.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "customers", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_customers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "pizzas", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + name = table.Column(type: "text", nullable: false), + price = table.Column(type: "numeric", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_pizzas", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "orders", + columns: table => new + { + id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + customer_id = table.Column(type: "integer", nullable: false), + pizza_id = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_orders", x => x.id); + table.ForeignKey( + name: "FK_orders_customers_customer_id", + column: x => x.customer_id, + principalTable: "customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_orders_pizzas_pizza_id", + column: x => x.pizza_id, + principalTable: "pizzas", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_orders_customer_id", + table: "orders", + column: "customer_id"); + + migrationBuilder.CreateIndex( + name: "IX_orders_pizza_id", + table: "orders", + column: "pizza_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "orders"); + + migrationBuilder.DropTable( + name: "customers"); + + migrationBuilder.DropTable( + name: "pizzas"); + } + } +} diff --git a/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs b/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs new file mode 100644 index 0000000..1c1676c --- /dev/null +++ b/exercise.pizzashopapi/Migrations/DataContextModelSnapshot.cs @@ -0,0 +1,121 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using exercise.pizzashopapi.Data; + +#nullable disable + +namespace exercise.pizzashopapi.Migrations +{ + [DbContext(typeof(DataContext))] + partial class DataContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("customers"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CustomerId") + .HasColumnType("integer") + .HasColumnName("customer_id"); + + b.Property("PizzaId") + .HasColumnType("integer") + .HasColumnName("pizza_id"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("PizzaId"); + + b.ToTable("orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.Property("Price") + .HasColumnType("numeric") + .HasColumnName("price"); + + b.HasKey("Id"); + + b.ToTable("pizzas"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Order", b => + { + b.HasOne("exercise.pizzashopapi.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("exercise.pizzashopapi.Models.Pizza", "Pizza") + .WithMany("Orders") + .HasForeignKey("PizzaId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Pizza"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("exercise.pizzashopapi.Models.Pizza", b => + { + b.Navigation("Orders"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/exercise.pizzashopapi/Models/Customer.cs b/exercise.pizzashopapi/Models/Customer.cs index 2ca83bd..e0cc205 100644 --- a/exercise.pizzashopapi/Models/Customer.cs +++ b/exercise.pizzashopapi/Models/Customer.cs @@ -1,10 +1,18 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { + [Table("customers")] public class Customer { + + [Key] public int Id { get; set; } + + [Column("name")] public string Name { get; set; } + + public List Orders { get; set; } = new List(); } } diff --git a/exercise.pizzashopapi/Models/Order.cs b/exercise.pizzashopapi/Models/Order.cs index fbe6113..9823766 100644 --- a/exercise.pizzashopapi/Models/Order.cs +++ b/exercise.pizzashopapi/Models/Order.cs @@ -1,10 +1,33 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { + [Table("orders")] public class Order { - + [Key] + [Column("id")] + public int Id { get; set; } + + [Column("customer_id")] + [ForeignKey("Customer")] + public int CustomerId { get; set; } + + [Column("pizza_id")] + [ForeignKey("Pizza")] + public int PizzaId { get; set; } + + //[Column("quantity")] + //public int Quantity { get; set; } + + //[Column("price")] + //public decimal Price { get; set; } + + public Pizza Pizza { get; set; } + public Customer Customer { get; set; } + + } } diff --git a/exercise.pizzashopapi/Models/Pizza.cs b/exercise.pizzashopapi/Models/Pizza.cs index 5c085ec..3be76a5 100644 --- a/exercise.pizzashopapi/Models/Pizza.cs +++ b/exercise.pizzashopapi/Models/Pizza.cs @@ -1,12 +1,20 @@ -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace exercise.pizzashopapi.Models { - + + [Table("pizzas")] public class Pizza - { + { + [Key] public int Id { get; set; } + + [Column("name")] public string Name { get; set; } + + [Column("price")] public decimal Price { get; set; } + public List Orders { get; set; } = new List(); } } \ No newline at end of file diff --git a/exercise.pizzashopapi/Program.cs b/exercise.pizzashopapi/Program.cs index c04a440..a560344 100644 --- a/exercise.pizzashopapi/Program.cs +++ b/exercise.pizzashopapi/Program.cs @@ -1,5 +1,7 @@ using exercise.pizzashopapi.Data; +using exercise.pizzashopapi.DTO; using exercise.pizzashopapi.EndPoints; +using exercise.pizzashopapi.Models; using exercise.pizzashopapi.Repository; var builder = WebApplication.CreateBuilder(args); @@ -7,11 +9,18 @@ // Add services to the container. builder.Services.AddControllers(); -builder.Services.AddScoped(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); + + + builder.Services.AddDbContext(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddAutoMapper(typeof(Program)); var app = builder.Build(); diff --git a/exercise.pizzashopapi/Repository/IRepository.cs b/exercise.pizzashopapi/Repository/IRepository.cs index b114ea8..850a665 100644 --- a/exercise.pizzashopapi/Repository/IRepository.cs +++ b/exercise.pizzashopapi/Repository/IRepository.cs @@ -1,11 +1,23 @@ -using exercise.pizzashopapi.Models; +using System.Linq.Expressions; +using exercise.pizzashopapi.Models; namespace exercise.pizzashopapi.Repository { - public interface IRepository + public interface IRepository { - Task> GetOrdersByCustomer(int id); - + Task> Get(); + Task GetById(int id); + + Task Add(T entity); + Task Update(T entity); + Task Delete(object id); + Task Save(); + Task> GetWithIncludes(params Expression>[] includes); + Task GetByIdWithIncludes(int id, params Expression>[] includes); + Task> GetWithNestedIncludes(params Func, IQueryable>[] includeActions); + + IQueryable GetQuery(); + } } diff --git a/exercise.pizzashopapi/Repository/Repository.cs b/exercise.pizzashopapi/Repository/Repository.cs index e109fce..a0240e1 100644 --- a/exercise.pizzashopapi/Repository/Repository.cs +++ b/exercise.pizzashopapi/Repository/Repository.cs @@ -1,14 +1,95 @@ -using exercise.pizzashopapi.Data; +using System.Linq.Expressions; +using exercise.pizzashopapi.Data; using exercise.pizzashopapi.Models; +using Microsoft.EntityFrameworkCore; namespace exercise.pizzashopapi.Repository { - public class Repository : IRepository + public class Repository : IRepository where T : class { private DataContext _db; - public Task> GetOrdersByCustomer(int id) + private DbSet _table = null!; + public Repository(DataContext db) { - throw new NotImplementedException(); + _db = db; + _table = _db.Set(); + } + + public async Task Add(T entity) + { + _table.Add(entity); + _db.SaveChangesAsync(); + return entity; + } + + public async Task Delete(object id) + { + T existing = _table.Find(id); + _table.Remove(existing); + _db.SaveChanges(); + return existing; + } + + public async Task> Get() + { + return _table.ToList(); + } + + public async Task GetById(int id) + { + return _table.Find(id); + } + + public async Task> GetWithIncludes(params Expression>[] includes) + { + IQueryable query = _table; + foreach (var include in includes) + { + query = query.Include(include); + } + return await query.ToListAsync(); + } + + public async Task> GetWithNestedIncludes(params Func, IQueryable>[] includeActions) + { + IQueryable query = _table; + + foreach (var includeAction in includeActions) + { + query = includeAction(query); + } + + return await query.ToListAsync(); + } + + public async Task GetByIdWithIncludes(int id, params Expression>[] includes) + { + IQueryable query = _table; + + foreach (var include in includes) + { + query = query.Include(include); + } + + return await query.FirstOrDefaultAsync(entity => EF.Property(entity, "Id") == id); + } + + public async Task Save() + { + _db.SaveChangesAsync(); + } + + public async Task Update(T entity) + { + _table.Attach(entity); + _db.Entry(entity).State = EntityState.Modified; + _db.SaveChanges(); + return entity; + } + + public IQueryable GetQuery() + { + return _table; } } } diff --git a/exercise.pizzashopapi/exercise.pizzashopapi.csproj b/exercise.pizzashopapi/exercise.pizzashopapi.csproj index 624203b..7e7ef08 100644 --- a/exercise.pizzashopapi/exercise.pizzashopapi.csproj +++ b/exercise.pizzashopapi/exercise.pizzashopapi.csproj @@ -1,4 +1,4 @@ - + net9.0 @@ -11,18 +11,23 @@ - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + + +