diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index cd5fc28..afd44e4 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -113,7 +113,7 @@ jobs: dotnet-version: '6.0.x' - name: Cache NuGet packages - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.2.2 with: path: ~/.nuget/packages key: nuget-ui-tests-${{ hashFiles('tests/RazorPagesMovie.UITests/RazorPagesMovie.UITests.csproj') }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 79fb4ff..8f49c65 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,7 +85,7 @@ jobs: - name: Cache NuGet packages if: matrix.build-mode == 'manual' - uses: actions/cache@v4.0.2 + uses: actions/cache@v4.2.2 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/global.json') }} @@ -150,7 +150,7 @@ jobs: dotnet-version: '6.0.x' - name: Cache NuGet packages - uses: actions/cache@v4.1.2 + uses: actions/cache@v4.2.2 with: path: ~/.nuget/packages key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/global.json') }} diff --git a/AKS-Release-Pipelines.webp b/AKS-Release-Pipelines.webp new file mode 100644 index 0000000..20cad28 Binary files /dev/null and b/AKS-Release-Pipelines.webp differ diff --git a/src/Data/RazorPagesMovieContext.cs b/src/Data/RazorPagesMovieContext.cs index 3663ec1..2e453d7 100644 --- a/src/Data/RazorPagesMovieContext.cs +++ b/src/Data/RazorPagesMovieContext.cs @@ -14,11 +14,20 @@ public RazorPagesMovieContext(DbContextOptions options) public DbSet Movie { get; set; } = default!; public DbSet Users { get; set; } = default!; + public DbSet Directors { get; set; } = default!; + public DbSet Reviews { get; set; } = default!; protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + // Director configuration + modelBuilder.Entity(entity => + { + entity.Property(e => e.Name).IsRequired().HasMaxLength(100); + entity.Property(e => e.BirthDate).IsRequired(); + }); + // User configuration modelBuilder.Entity(entity => { @@ -38,8 +47,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.Cascade); entity.HasIndex(m => m.UserId); - entity.Property(m => m.Timestamp).IsConcurrencyToken(); - entity.Property(m => m.Timestamp).HasDefaultValue(new byte[8]); // Set default value for Timestamp + entity.Property(m => m.Timestamp).IsRowVersion(); // Ensure Timestamp is configured as rowversion }); // Seed data with hashed passwords @@ -89,6 +97,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) Timestamp = new byte[8] }); } + + foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys())) + { + relationship.DeleteBehavior = DeleteBehavior.NoAction; // Or DeleteBehavior.Restrict + } } private static string HashPassword(string password) diff --git a/src/Migrations/20241201195812_InitialCreate.Designer.cs b/src/Migrations/20241216191308_InitialCreate.Designer.cs similarity index 98% rename from src/Migrations/20241201195812_InitialCreate.Designer.cs rename to src/Migrations/20241216191308_InitialCreate.Designer.cs index 8ac3ca4..bc864b7 100644 --- a/src/Migrations/20241201195812_InitialCreate.Designer.cs +++ b/src/Migrations/20241216191308_InitialCreate.Designer.cs @@ -12,7 +12,7 @@ namespace RazorPagesMovie.Migrations { [DbContext(typeof(RazorPagesMovieContext))] - [Migration("20241201195812_InitialCreate")] + [Migration("20241216191308_InitialCreate")] partial class InitialCreate { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -50,8 +50,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .IsConcurrencyToken() .IsRequired() .ValueGeneratedOnAddOrUpdate() - .HasColumnType("rowversion") - .HasDefaultValue(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }); + .HasColumnType("rowversion"); b.Property("Title") .IsRequired() diff --git a/src/Migrations/20241201195812_InitialCreate.cs b/src/Migrations/20241216191308_InitialCreate.cs similarity index 97% rename from src/Migrations/20241201195812_InitialCreate.cs rename to src/Migrations/20241216191308_InitialCreate.cs index 1331417..2db7c4d 100644 --- a/src/Migrations/20241201195812_InitialCreate.cs +++ b/src/Migrations/20241216191308_InitialCreate.cs @@ -37,7 +37,7 @@ protected override void Up(MigrationBuilder migrationBuilder) Price = table.Column(type: "decimal(18,2)", nullable: false), Rating = table.Column(type: "nvarchar(max)", nullable: false), UserId = table.Column(type: "int", nullable: true), - Timestamp = table.Column(type: "rowversion", rowVersion: true, nullable: false, defaultValue: new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }) + Timestamp = table.Column(type: "rowversion", rowVersion: true, nullable: false) }, constraints: table => { diff --git a/src/Migrations/20241216215338_AddDirectorTable.Designer.cs b/src/Migrations/20241216215338_AddDirectorTable.Designer.cs new file mode 100644 index 0000000..e7e31d8 --- /dev/null +++ b/src/Migrations/20241216215338_AddDirectorTable.Designer.cs @@ -0,0 +1,308 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RazorPagesMovie.Data; + +#nullable disable + +namespace RazorPagesMovie.Migrations +{ + [DbContext(typeof(RazorPagesMovieContext))] + [Migration("20241216215338_AddDirectorTable")] + partial class AddDirectorTable + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("RazorPagesMovie.Models.Director", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("BirthDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("Id"); + + b.ToTable("Directors"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Genre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Password") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Role") + .HasColumnType("int"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + + b.HasData( + new + { + Id = 1, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin" + }, + new + { + Id = 2, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin1" + }, + new + { + Id = 3, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin2" + }, + new + { + Id = 4, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin3" + }, + new + { + Id = 5, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin4" + }, + new + { + Id = 6, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin5" + }, + new + { + Id = 7, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin6" + }, + new + { + Id = 8, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin7" + }, + new + { + Id = 9, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin8" + }, + new + { + Id = 10, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin9" + }, + new + { + Id = 11, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user" + }, + new + { + Id = 12, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user1" + }, + new + { + Id = 13, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user2" + }, + new + { + Id = 14, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user3" + }, + new + { + Id = 15, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user4" + }, + new + { + Id = 16, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user5" + }, + new + { + Id = 17, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user6" + }, + new + { + Id = 18, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user7" + }, + new + { + Id = 19, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user8" + }, + new + { + Id = 20, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user9" + }); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.Movie", b => + { + b.HasOne("RazorPagesMovie.Models.User", "User") + .WithMany("Movies") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.User", b => + { + b.Navigation("Movies"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Migrations/20241216215338_AddDirectorTable.cs b/src/Migrations/20241216215338_AddDirectorTable.cs new file mode 100644 index 0000000..dd91dab --- /dev/null +++ b/src/Migrations/20241216215338_AddDirectorTable.cs @@ -0,0 +1,34 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace RazorPagesMovie.Migrations +{ + public partial class AddDirectorTable : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Directors", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(100)", maxLength: 100, nullable: false), + BirthDate = table.Column(type: "datetime2", nullable: false), + Timestamp = table.Column(type: "rowversion", rowVersion: true, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Directors", x => x.Id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Directors"); + } + } +} diff --git a/src/Migrations/20241216222317_AddReviews.Designer.cs b/src/Migrations/20241216222317_AddReviews.Designer.cs new file mode 100644 index 0000000..8b79079 --- /dev/null +++ b/src/Migrations/20241216222317_AddReviews.Designer.cs @@ -0,0 +1,363 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RazorPagesMovie.Data; + +#nullable disable + +namespace RazorPagesMovie.Migrations +{ + [DbContext(typeof(RazorPagesMovieContext))] + [Migration("20241216222317_AddReviews")] + partial class AddReviews + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1); + + modelBuilder.Entity("RazorPagesMovie.Models.Director", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("BirthDate") + .HasColumnType("datetime2"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.HasKey("Id"); + + b.ToTable("Directors"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.Movie", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Genre") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(18,2)"); + + b.Property("Rating") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReleaseDate") + .HasColumnType("datetime2"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("Movie"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Comment") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MovieId") + .HasColumnType("int"); + + b.Property("Rating") + .HasColumnType("int"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .IsRequired() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("MovieId"); + + b.HasIndex("UserId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id"), 1L, 1); + + b.Property("Password") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("nvarchar(100)"); + + b.Property("Role") + .HasColumnType("int"); + + b.Property("Timestamp") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("rowversion"); + + b.Property("Username") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("nvarchar(50)"); + + b.HasKey("Id"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + + b.HasData( + new + { + Id = 1, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin" + }, + new + { + Id = 2, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin1" + }, + new + { + Id = 3, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin2" + }, + new + { + Id = 4, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin3" + }, + new + { + Id = 5, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin4" + }, + new + { + Id = 6, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin5" + }, + new + { + Id = 7, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin6" + }, + new + { + Id = 8, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin7" + }, + new + { + Id = 9, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin8" + }, + new + { + Id = 10, + Password = "password", + Role = 2, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "admin9" + }, + new + { + Id = 11, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user" + }, + new + { + Id = 12, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user1" + }, + new + { + Id = 13, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user2" + }, + new + { + Id = 14, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user3" + }, + new + { + Id = 15, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user4" + }, + new + { + Id = 16, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user5" + }, + new + { + Id = 17, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user6" + }, + new + { + Id = 18, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user7" + }, + new + { + Id = 19, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user8" + }, + new + { + Id = 20, + Password = "password", + Role = 0, + Timestamp = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + Username = "user9" + }); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.Movie", b => + { + b.HasOne("RazorPagesMovie.Models.User", "User") + .WithMany("Movies") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.Review", b => + { + b.HasOne("RazorPagesMovie.Models.Movie", "Movie") + .WithMany() + .HasForeignKey("MovieId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("RazorPagesMovie.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Movie"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("RazorPagesMovie.Models.User", b => + { + b.Navigation("Movies"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Migrations/20241216222317_AddReviews.cs b/src/Migrations/20241216222317_AddReviews.cs new file mode 100644 index 0000000..fad64c0 --- /dev/null +++ b/src/Migrations/20241216222317_AddReviews.cs @@ -0,0 +1,79 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace RazorPagesMovie.Migrations +{ + public partial class AddReviews : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Movie_Users_UserId", + table: "Movie"); + + migrationBuilder.CreateTable( + name: "Reviews", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + MovieId = table.Column(type: "int", nullable: false), + UserId = table.Column(type: "int", nullable: false), + Rating = table.Column(type: "int", nullable: false), + Comment = table.Column(type: "nvarchar(max)", nullable: false), + Timestamp = table.Column(type: "rowversion", rowVersion: true, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Reviews", x => x.Id); + table.ForeignKey( + name: "FK_Reviews_Movie_MovieId", + column: x => x.MovieId, + principalTable: "Movie", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Reviews_Users_UserId", + column: x => x.UserId, + principalTable: "Users", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Reviews_MovieId", + table: "Reviews", + column: "MovieId"); + + migrationBuilder.CreateIndex( + name: "IX_Reviews_UserId", + table: "Reviews", + column: "UserId"); + + migrationBuilder.AddForeignKey( + name: "FK_Movie_Users_UserId", + table: "Movie", + column: "UserId", + principalTable: "Users", + principalColumn: "Id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Movie_Users_UserId", + table: "Movie"); + + migrationBuilder.DropTable( + name: "Reviews"); + + migrationBuilder.AddForeignKey( + name: "FK_Movie_Users_UserId", + table: "Movie", + column: "UserId", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/src/Models/Director.cs b/src/Models/Director.cs new file mode 100644 index 0000000..7a4b851 --- /dev/null +++ b/src/Models/Director.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RazorPagesMovie.Models +{ + public class Director + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + public DateTime BirthDate { get; set; } + [Timestamp] + public byte[]? Timestamp { get; set; } + } +} \ No newline at end of file diff --git a/src/Models/Review.cs b/src/Models/Review.cs new file mode 100644 index 0000000..5216832 --- /dev/null +++ b/src/Models/Review.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RazorPagesMovie.Models +{ + public class Review + { + public int Id { get; set; } + + [Required] + public int MovieId { get; set; } + [ForeignKey("MovieId")] // Add ForeignKey attribute + public Movie? Movie { get; set; } // Navigation property + + [Required] + public int UserId { get; set; } + [ForeignKey("UserId")] // Add ForeignKey attribute + public User? User { get; set; } // Navigation property + + [Range(1, 5)] + public int Rating { get; set; } + + public string Comment { get; set; } = string.Empty; + + [Timestamp] + public byte[] Timestamp { get; set; } = new byte[8]; + } +} \ No newline at end of file