diff --git a/Screenshot 2025-01-22 100528.png b/Screenshot 2025-01-22 100528.png new file mode 100644 index 00000000..ed31f695 Binary files /dev/null and b/Screenshot 2025-01-22 100528.png differ diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs new file mode 100644 index 00000000..2964ab8e --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/CustomerDTO.cs @@ -0,0 +1,35 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class CustomerDTO + { + [Column("customerId")] + public int customerId { get; set; } + [Column("Name")] + public string Name { get; set; } + [Column("Email")] + public string Email { get; set; } + [Column("Phone")] + public string Phone { get; set; } + [Column("CreatedAt")] + public string CreatedAt { get; set; } + [Column("UpdatedAt")] + public string UpdatedAt { get; set; } + [Column("tickets")] + public virtual List tickets { get; set; } = new List(); + + public CustomerDTO(Customer customer) + { + customerId = customer.customerId; + Name = customer.Name; + Email = customer.Email; + Phone = customer.Phone; + CreatedAt = customer.CreatedAt.ToString(); + UpdatedAt = customer.UpdatedAt.ToString(); + //making ticket dtos + customer.tickets.ForEach(x => tickets.Add($" screenNumber: {x.screen.screenNumber} ")); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs new file mode 100644 index 00000000..f31c3205 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/MovieDTO.cs @@ -0,0 +1,29 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class MovieDTO + { + public int movieId { get; set; } + public string Title { get; set; } + public string Rating { get; set; } + public string Description { get; set; } + public string RuntimeMins { get; set; } + public string CreatedAt { get; set; } + public string UpdatedAt { get; set; } + public virtual List screens { get; set; } = new List(); + + public MovieDTO(Movie movie) + { + movieId = movie.movieId; + Title = movie.Title; + Rating = movie.Rating; + Description = movie.Description; + RuntimeMins = movie.RuntimeMins; + CreatedAt = movie.CreatedAt.ToString(); + UpdatedAt = movie.UpdatedAt.ToString(); + movie.screens.ForEach(x => screens.Add(x.screenNumber.ToString())); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs new file mode 100644 index 00000000..d4a57d4f --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/ScreenDTO.cs @@ -0,0 +1,33 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.DTO +{ + public class ScreenDTO + { + public int screenId { get; set; } + public int screenNumber { get; set; } + public int capacity { get; set; } + public string startsAt { get; set; } + public string createdAt { get; set; } + public string updatedAt { get; set; } + public virtual List tickets { get; set; } + public virtual string movie { get; set; } + + public ScreenDTO(Screen screen) + { + screenId = screen.screenId; + screenNumber = screen.screenNumber; + capacity = screen.capacity; + startsAt = screen.startsAt; + createdAt = screen.createdAt; + updatedAt = screen.updatedAt; + tickets = new List(); + + //convert tickets and movies to strings + screen.tickets.ForEach(x => tickets.Add($" ticket id : {x.ticketId}, customer: {x.customer.Name}")); + movie = $" movie id {screen.movieId}, movie title {screen.movie.Title}"; + + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs new file mode 100644 index 00000000..d72ba3da --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/DTO/TicketDTO.cs @@ -0,0 +1,31 @@ +using api_cinema_challenge.Models; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; + +namespace api_cinema_challenge.DTO +{ + public class TicketDTO + { + + public int ticketId { get; set; } + + public int screenNumber { get; set; } + + public int customerID { get; set; } + [JsonIgnore] + public string customer { get; set; } + + + + + public TicketDTO(Ticket ticket) + { + ticketId = ticket.ticketId; + screenNumber = ticket.screen.screenNumber; + customerID = ticket.customerID; + //fill in customer + customer = ticket.customer.Name.ToString(); + } + } + +} diff --git a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs index ad4fe854..a8e0a77c 100644 --- a/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs +++ b/api-cinema-challenge/api-cinema-challenge/Data/CinemaContext.cs @@ -1,6 +1,10 @@ -using Microsoft.EntityFrameworkCore; +using api_cinema_challenge.DTO; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.JSInterop.Infrastructure; using Newtonsoft.Json.Linq; + namespace api_cinema_challenge.Data { public class CinemaContext : DbContext @@ -10,17 +14,96 @@ public CinemaContext(DbContextOptions options) : base(options) { var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build(); _connectionString = configuration.GetValue("ConnectionStrings:DefaultConnectionString")!; + this.Database.SetConnectionString(_connectionString); this.Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseNpgsql(_connectionString); + optionsBuilder.UseLazyLoadingProxies(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { + //keys + modelBuilder.Entity() + .HasKey(a => a.customerId); + + modelBuilder.Entity() + .HasKey(a => a.movieId); + + modelBuilder.Entity() + .HasKey(a => a.screenId); + + modelBuilder.Entity() + .HasKey(a => a.ticketId); + + + //defining relations + modelBuilder.Entity() + .HasMany(a => a.tickets) + .WithOne(a => a.customer); + + modelBuilder.Entity() + .HasOne(a => a.customer) + .WithMany(a => a.tickets) + .HasForeignKey(a => a.customerID); + + modelBuilder.Entity() + .HasOne(a => a.screen) + .WithMany(a => a.tickets) + .HasForeignKey(a => a.screenId); + + modelBuilder.Entity() + .HasOne(a => a.movie) + .WithMany(a => a.screens); + + modelBuilder.Entity() + .HasMany(a => a.tickets) + .WithOne(a => a.screen); + + + modelBuilder.Entity() + .HasMany(a => a.screens) + .WithOne(a => a.movie); + + //seeding + modelBuilder.Entity() + .HasData( + new List + { + new Movie { movieId=1 ,Title = "the hobbit",UpdatedAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), CreatedAt =DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() ,Rating = "10",Description= "a lonely hobbit travels the world together with his trusty friends gollum and sauron", RuntimeMins="120"} + } + ); + modelBuilder.Entity() + .HasData( + new List + { + new Customer {customerId =1, Name="bob" , CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), Email="bob@gmail.com", Phone="12345678" , UpdatedAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString()} + } + ); + modelBuilder.Entity() + .HasData( + new List + { + new Screen {screenId=1, capacity=2, movieId=1,createdAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), screenNumber=1, startsAt=DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), updatedAt= DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString()} + } + ); + modelBuilder.Entity() + .HasData( + new List + { + new Ticket {ticketId=1, customerID=1, screenId=1} + } + ); + } + public DbSet Movies { get; set; } + public DbSet Customers { get; set; } + public DbSet Screens { get; set; } + public DbSet Tickets { get; set; } + } } diff --git a/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs new file mode 100644 index 00000000..392c2799 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Endpoints/CinemaEndpoint.cs @@ -0,0 +1,241 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.Globalization; +using System.Reflection.Emit; +using api_cinema_challenge.DTO; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc; + +namespace api_cinema_challenge.Endpoints +{ + public static class CinemaEndpoint + { + public static void ConfigureCinema(this WebApplication app) + { + var cinema = app.MapGroup("/cinema"); + + cinema.MapGet("/movies", GetAllMovies); + cinema.MapPost("/newmovie", CreateMovie); + cinema.MapPut("/updatemovie", UpdateMovie); + cinema.MapDelete("/deletemovie", DeleteMovie); + + cinema.MapGet("/customers", GetAllCustomers); + cinema.MapPost("/newcustomer", CreateCustomer); + cinema.MapPut("/updatecustomer", UpdateCustomer); + cinema.MapDelete("/deletecustomer", DeleteCustomer); + + cinema.MapGet("/tickets", GetAllTickets); + cinema.MapPost("/newticket", CreateTicket); + + + cinema.MapGet("/screens", GetAllScreens); + cinema.MapPost("/newscreen", CreateScreen); + + + } + #region Movies + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetAllMovies(IRepository movieRepo) + { + var results = movieRepo.GetAll(); + List movieDTOs = new List(); + results.ToList().ForEach(x => movieDTOs.Add(new MovieDTO(x))); + return TypedResults.Ok(movieDTOs); + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateMovie(IRepository repo, string title, string runtime, string description, string rating) + { + + try + { + Movie movie = new Movie { CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), Description = description, Title = title, RuntimeMins = runtime, Rating = rating, UpdatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() }; + repo.Insert(movie); + repo.Save(); + return TypedResults.Ok(movie); + } + catch (Exception e) + { + return TypedResults.BadRequest(e); + } + + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task UpdateMovie(IRepository repo, int id, string title, string runtime, string description, string rating) + { + try + { + Movie movie = repo.GetById(id); + movie.Title = title; + movie.Description = description; + movie.RuntimeMins = runtime; + movie.Rating = rating; + movie.UpdatedAt = DateTime.Now.ToString(); + + repo.Save(); + return TypedResults.Ok(new MovieDTO(movie)); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task DeleteMovie(IRepository repo, int id) + { + try + { + repo.Delete(id); + + repo.Save(); + return TypedResults.Ok(); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } + + + #endregion + + #region Customers + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetAllCustomers(IRepository customerRepo) + { + var results = customerRepo.GetAll(); + List customerDTOs = new List(); + results.ToList().ForEach(x => customerDTOs.Add(new CustomerDTO(x))); + return TypedResults.Ok(customerDTOs); + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateCustomer(IRepository repo, string name, string email, string phone) + { + + try + { + Customer customer = new Customer { CreatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), Name = name, Email = email, Phone = phone, UpdatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() }; + repo.Insert(customer); + repo.Save(); + return TypedResults.Ok(customer); + } + catch (Exception e) + { + return TypedResults.BadRequest(e); + } + + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task UpdateCustomer(IRepository repo, int id, string name, string email, string phone) + { + try + { + Customer customer = repo.GetById(id); + customer.Name = name; + customer.Email = email; + customer.Phone = phone; + customer.UpdatedAt = DateTime.Now.ToString(); + + repo.Save(); + return TypedResults.Ok(new CustomerDTO(customer)); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task DeleteCustomer(IRepository repo, int id) + { + try + { + repo.Delete(id); + + repo.Save(); + return TypedResults.Ok(); + } + catch (Exception ex) + { + return TypedResults.BadRequest(ex); + } + } + #endregion + #region Tickets + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetAllTickets(IRepository ticketRepo) + { + var results = ticketRepo.GetAll(); + List ticketDTOs = new List(); + results.ToList().ForEach(x => ticketDTOs.Add(new TicketDTO(x))); + return TypedResults.Ok(ticketDTOs); + } + + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateTicket(IRepository repo, int customerId, int screenId) + { + + + Ticket ticket = new Ticket { customerID = customerId, screenId = screenId }; + repo.Insert(ticket); + repo.Save(); + return TypedResults.Ok(); + + + + } + + + + + #endregion + #region Screens + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetAllScreens(IRepository screenRepo) + { + var results = screenRepo.GetAll(); + List screenDTOs = new List(); + results.ToList().ForEach(x => screenDTOs.Add(new ScreenDTO(x))); + return TypedResults.Ok(screenDTOs); + } + #endregion + #region MovieScreenings + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task GetAllScreenings(IRepository screeningRepo) + { + var results = screeningRepo.GetAll(); + List screeningDTOs = new List(); + results.ToList().ForEach(x => screeningDTOs.Add(new ScreenDTO(x))); + return TypedResults.Ok(screeningDTOs); + } + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public static async Task CreateScreen(IRepository repo, int capacity, int screenNumber, string startsAt, int movieId) + { + try + { + Screen screen = new Screen { capacity = capacity, movieId = movieId, createdAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), screenNumber = screenNumber, startsAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString(), updatedAt = DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Utc).ToString() }; + repo.Insert(screen); + repo.Save(); + return TypedResults.Ok(); + } + catch (Exception e) + { + return TypedResults.BadRequest(e); + } + + #endregion + } + } +} \ No newline at end of file diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.Designer.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.Designer.cs new file mode 100644 index 00000000..9c85e509 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.Designer.cs @@ -0,0 +1,222 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using api_cinema_challenge.Data; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + [DbContext(typeof(CinemaContext))] + [Migration("20250129150313_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MovieScreen", b => + { + b.Property("moviesmovieId") + .HasColumnType("integer"); + + b.Property("screensscreenId") + .HasColumnType("integer"); + + b.HasKey("moviesmovieId", "screensscreenId"); + + b.HasIndex("screensscreenId"); + + b.ToTable("MovieScreen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("customerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("customerId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("customerId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Email"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Phone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("customerId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("movieId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("movieId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("movieId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Description"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Rating"); + + b.Property("RuntimeMins") + .IsRequired() + .HasColumnType("text") + .HasColumnName("RuntimeMins"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("movieId"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Property("screenId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("screenId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("screenId")); + + b.Property("capacity") + .HasColumnType("integer") + .HasColumnName("capacity"); + + b.Property("createdAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("createdAt"); + + b.Property("screenNumber") + .HasColumnType("integer") + .HasColumnName("screenNumber"); + + b.Property("startsAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("startsAt"); + + b.Property("updatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("screenId"); + + b.ToTable("Screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("ticketId") + .HasColumnType("integer") + .HasColumnName("ticketId"); + + b.Property("customerID") + .HasColumnType("integer") + .HasColumnName("customerId"); + + b.HasKey("ticketId"); + + b.HasIndex("customerID"); + + b.ToTable("Ticket"); + }); + + modelBuilder.Entity("MovieScreen", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", null) + .WithMany() + .HasForeignKey("moviesmovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screen", null) + .WithMany() + .HasForeignKey("screensscreenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.HasOne("api_cinema_challenge.Models.Customer", "customer") + .WithMany("tickets") + .HasForeignKey("customerID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screen", "screen") + .WithMany("tickets") + .HasForeignKey("ticketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("customer"); + + b.Navigation("screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Navigation("tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.cs new file mode 100644 index 00000000..46cc8ca4 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/20250129150313_InitialCreate.cs @@ -0,0 +1,145 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Customer", + columns: table => new + { + customerId = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + Phone = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Customer", x => x.customerId); + }); + + migrationBuilder.CreateTable( + name: "Movie", + columns: table => new + { + movieId = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Title = table.Column(type: "text", nullable: false), + Rating = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: false), + RuntimeMins = table.Column(type: "text", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Movie", x => x.movieId); + }); + + migrationBuilder.CreateTable( + name: "Screen", + columns: table => new + { + screenId = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + screenNumber = table.Column(type: "integer", nullable: false), + capacity = table.Column(type: "integer", nullable: false), + startsAt = table.Column(type: "timestamp with time zone", nullable: false), + createdAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Screen", x => x.screenId); + }); + + migrationBuilder.CreateTable( + name: "MovieScreen", + columns: table => new + { + moviesmovieId = table.Column(type: "integer", nullable: false), + screensscreenId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MovieScreen", x => new { x.moviesmovieId, x.screensscreenId }); + table.ForeignKey( + name: "FK_MovieScreen_Movie_moviesmovieId", + column: x => x.moviesmovieId, + principalTable: "Movie", + principalColumn: "movieId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MovieScreen_Screen_screensscreenId", + column: x => x.screensscreenId, + principalTable: "Screen", + principalColumn: "screenId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Ticket", + columns: table => new + { + ticketId = table.Column(type: "integer", nullable: false), + customerId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Ticket", x => x.ticketId); + table.ForeignKey( + name: "FK_Ticket_Customer_customerId", + column: x => x.customerId, + principalTable: "Customer", + principalColumn: "customerId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Ticket_Screen_ticketId", + column: x => x.ticketId, + principalTable: "Screen", + principalColumn: "screenId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_MovieScreen_screensscreenId", + table: "MovieScreen", + column: "screensscreenId"); + + migrationBuilder.CreateIndex( + name: "IX_Ticket_customerId", + table: "Ticket", + column: "customerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "MovieScreen"); + + migrationBuilder.DropTable( + name: "Ticket"); + + migrationBuilder.DropTable( + name: "Movie"); + + migrationBuilder.DropTable( + name: "Customer"); + + migrationBuilder.DropTable( + name: "Screen"); + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs new file mode 100644 index 00000000..28ef1002 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Migrations/CinemaContextModelSnapshot.cs @@ -0,0 +1,219 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using api_cinema_challenge.Data; + +#nullable disable + +namespace api_cinema_challenge.Migrations +{ + [DbContext(typeof(CinemaContext))] + partial class CinemaContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.1") + .HasAnnotation("Proxies:ChangeTracking", false) + .HasAnnotation("Proxies:CheckEquality", false) + .HasAnnotation("Proxies:LazyLoading", true) + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("MovieScreen", b => + { + b.Property("moviesmovieId") + .HasColumnType("integer"); + + b.Property("screensscreenId") + .HasColumnType("integer"); + + b.HasKey("moviesmovieId", "screensscreenId"); + + b.HasIndex("screensscreenId"); + + b.ToTable("MovieScreen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Property("customerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("customerId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("customerId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Email"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Name"); + + b.Property("Phone") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Phone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("customerId"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Movie", b => + { + b.Property("movieId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("movieId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("movieId")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreatedAt"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Description"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Rating"); + + b.Property("RuntimeMins") + .IsRequired() + .HasColumnType("text") + .HasColumnName("RuntimeMins"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("Title"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("movieId"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Property("screenId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("screenId"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("screenId")); + + b.Property("capacity") + .HasColumnType("integer") + .HasColumnName("capacity"); + + b.Property("createdAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("createdAt"); + + b.Property("screenNumber") + .HasColumnType("integer") + .HasColumnName("screenNumber"); + + b.Property("startsAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("startsAt"); + + b.Property("updatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("UpdatedAt"); + + b.HasKey("screenId"); + + b.ToTable("Screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.Property("ticketId") + .HasColumnType("integer") + .HasColumnName("ticketId"); + + b.Property("customerID") + .HasColumnType("integer") + .HasColumnName("customerId"); + + b.HasKey("ticketId"); + + b.HasIndex("customerID"); + + b.ToTable("Ticket"); + }); + + modelBuilder.Entity("MovieScreen", b => + { + b.HasOne("api_cinema_challenge.Models.Movie", null) + .WithMany() + .HasForeignKey("moviesmovieId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screen", null) + .WithMany() + .HasForeignKey("screensscreenId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Ticket", b => + { + b.HasOne("api_cinema_challenge.Models.Customer", "customer") + .WithMany("tickets") + .HasForeignKey("customerID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("api_cinema_challenge.Models.Screen", "screen") + .WithMany("tickets") + .HasForeignKey("ticketId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("customer"); + + b.Navigation("screen"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Customer", b => + { + b.Navigation("tickets"); + }); + + modelBuilder.Entity("api_cinema_challenge.Models.Screen", b => + { + b.Navigation("tickets"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs new file mode 100644 index 00000000..004b8417 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Customer.cs @@ -0,0 +1,29 @@ +using Microsoft.EntityFrameworkCore; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api_cinema_challenge.Models +{ + [Table("Customer")] + [PrimaryKey("customerId")] + public class Customer + { + [Column("customerId")] + public int customerId { get; set; } + [Column("Name")] + public string Name { get; set; } + [Column("Email")] + public string Email { get; set; } + [Column("Phone")] + public string Phone { get; set; } + [Column("CreatedAt")] + public string CreatedAt { get; set; } + [Column("UpdatedAt")] + public string UpdatedAt { get; set; } + [NotMapped] + public virtual List tickets { get; set; } = new List(); + [NotMapped] + public int ticketId { get; set; } + + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs new file mode 100644 index 00000000..888b6181 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Movie.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Models +{ + [Table("Movie")] + [PrimaryKey("movieId")] + public class Movie + { + [Column("movieId")] + public int movieId { get; set; } + [Column("Title")] + public string Title { get; set; } + [Column("Rating")] + public string Rating { get; set; } + [Column("Description")] + public string Description { get; set; } + [Column("RuntimeMins")] + public string RuntimeMins { get; set; } + [Column("CreatedAt")] + public string CreatedAt { get; set; } + [Column("UpdatedAt")] + public string UpdatedAt { get; set; } + [NotMapped] + public virtual List screens { get; set; } + + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs new file mode 100644 index 00000000..05dc1e1d --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Screen.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations.Schema; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Models +{ + [Table("Screen")] + [PrimaryKey("screenId")] + public class Screen + { + [Column("screenId")] + public int screenId { get; set; } + [Column("screenNumber")] + public int screenNumber { get; set; } + [Column("capacity")] + public int capacity { get; set; } + [Column("startsAt")] + public string startsAt { get; set; } + [Column("createdAt")] + public string createdAt { get; set; } + [Column("UpdatedAt")] + public string updatedAt { get; set; } + [Column("movieId")] + public int movieId{ get; set; } + [NotMapped] + public virtual Movie movie{ get; set; } + [NotMapped] + public virtual List tickets{ get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs new file mode 100644 index 00000000..8554a4d2 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Models/Ticket.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json.Serialization; +using Microsoft.EntityFrameworkCore; + +namespace api_cinema_challenge.Models +{ + //你看不懂这个吧 + [Table("Ticket")] + [PrimaryKey("ticketId")] + public class Ticket + { + [Column("ticketId")] + public int ticketId { get; set; } + [NotMapped] + public virtual Screen screen { get; set; } + [Column("screenId")] + public int screenId { get; set; } + [Column("customerId")] + public int customerID { get; set; } + [NotMapped] + + public virtual Customer customer { get; set; } + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/Program.cs b/api-cinema-challenge/api-cinema-challenge/Program.cs index e55d9d54..fefb1054 100644 --- a/api-cinema-challenge/api-cinema-challenge/Program.cs +++ b/api-cinema-challenge/api-cinema-challenge/Program.cs @@ -1,4 +1,7 @@ using api_cinema_challenge.Data; +using api_cinema_challenge.Endpoints; +using api_cinema_challenge.Models; +using api_cinema_challenge.Repository; var builder = WebApplication.CreateBuilder(args); @@ -6,6 +9,11 @@ builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddDbContext(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); +builder.Services.AddScoped, Repository>(); + +builder.Services.AddScoped, Repository>(); var app = builder.Build(); @@ -17,4 +25,5 @@ } app.UseHttpsRedirection(); +app.ConfigureCinema(); app.Run(); diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs new file mode 100644 index 00000000..f980176d --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/IRepository.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore; +using System.Linq.Expressions; + +namespace api_cinema_challenge.Repository +{ + public interface IRepository where T : class + { + public IEnumerable GetAll(); + IEnumerable GetAll(params Expression>[] includeExpressions); + T GetById(object id); + void Insert(T obj); + void Update(T obj); + void Delete(object id); + void Save(); + DbSet Table { get; } + + } + +} diff --git a/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs new file mode 100644 index 00000000..9fc0cf84 --- /dev/null +++ b/api-cinema-challenge/api-cinema-challenge/Repository/Repository.cs @@ -0,0 +1,66 @@ +using System.Linq.Expressions; +using api_cinema_challenge.Data; +using api_cinema_challenge.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query.Internal; + +namespace api_cinema_challenge.Repository +{ + public class Repository : IRepository where T : class + { + + + private CinemaContext _db; + private DbSet _table = null; + + public Repository(CinemaContext db) + { + _db = db; + _table = _db.Set(); + } + + public IEnumerable GetAll(params Expression>[] includeExpressions) + { + if (includeExpressions.Any()) + { + var set = includeExpressions + .Aggregate>, IQueryable> + (_table, (current, expression) => current.Include(expression)); + } + return _table.ToList(); + } + + public IEnumerable GetAll() + { + return _table.ToList(); + } + public T GetById(object id) + { + return _table.Find(id); + } + + public void Insert(T obj) + { + _table.Add(obj); + } + public void Update(T obj) + { + _table.Attach(obj); + _db.Entry(obj).State = EntityState.Modified; + } + + public void Delete(object id) + { + T existing = _table.Find(id); + _table.Remove(existing); + } + + + public void Save() + { + _db.SaveChanges(); + } + public DbSet Table { get { return _table; } } + + } +} diff --git a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj index 8c888bf8..2a425aa2 100644 --- a/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj +++ b/api-cinema-challenge/api-cinema-challenge/api-cinema-challenge.csproj @@ -17,6 +17,7 @@ + all @@ -27,8 +28,4 @@ - - - -