diff --git a/.tool-versions b/.tool-versions index 63c2ed8..91cdd73 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -dotnet-core 8.0.414 +dotnet-core 8.0.413 diff --git a/ecosystem/database/1_core_language/setup.sh b/ecosystem/database/1_core_language/setup.sh index 0e0a392..62cbe6d 100755 --- a/ecosystem/database/1_core_language/setup.sh +++ b/ecosystem/database/1_core_language/setup.sh @@ -1,3 +1,11 @@ echo "Installing EF Global Tool" dotnet tool install -g dotnet-ef echo "Done Installing EF Global Tool" + +# Useful Commands +# 1. Create Migrations + # pattern: dotnet ef migrations add {Migration Name} +dotnet ef migrations add InitialCreate + +# 2. Run Migrations +dotnet ef database update \ No newline at end of file diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/.devcontainer/devcontainer.json b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8e63ad6 --- /dev/null +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/.devcontainer/devcontainer.json @@ -0,0 +1,16 @@ +{ + "name": "pizza_shop", + "build": { + "dockerfile": "../DockerFile.dev" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-dotnettools.csdevkit" + ], + "settings": {} + } + }, + "workspaceMount": "source=${localWorkspaceFolder},target=/pizza_shop,type=bind,consistency=delegated", + "workspaceFolder": "/pizza_shop" +} \ No newline at end of file diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/Dockerfile.dev b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/Dockerfile.dev new file mode 100644 index 0000000..dbf2d47 --- /dev/null +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/Dockerfile.dev @@ -0,0 +1,5 @@ +FROM mcr.microsoft.com/devcontainers/dotnet:8.0 + +RUN dotnet tool install --global dotnet-ef --version 8.0.0 + +ENV PATH="$PATH:/root/.dotnet/tools" \ No newline at end of file diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop.sln b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop.sln new file mode 100644 index 0000000..a4f3cf7 --- /dev/null +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pizza_shop", "pizza_shop\pizza_shop.csproj", "{70096DE8-8824-3D33-70A2-FE4E18953E5B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {70096DE8-8824-3D33-70A2-FE4E18953E5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70096DE8-8824-3D33-70A2-FE4E18953E5B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70096DE8-8824-3D33-70A2-FE4E18953E5B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70096DE8-8824-3D33-70A2-FE4E18953E5B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F5990F53-54B8-4D8D-A393-68F71D4BF4A0} + EndGlobalSection +EndGlobal diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/20251003024533_InitialCreate.Designer.cs b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/20251003024533_InitialCreate.Designer.cs new file mode 100644 index 0000000..3c0eb0b --- /dev/null +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/20251003024533_InitialCreate.Designer.cs @@ -0,0 +1,167 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PizzaShop.Data; + +#nullable disable + +namespace pizza_shop.Migrations +{ + [DbContext(typeof(PizzaShopContext))] + [Migration("20251003024533_InitialCreate")] + partial class InitialCreate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PizzaShop.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Phone") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("PizzaShop.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("OrderFulfilled") + .HasColumnType("datetime2"); + + b.Property("OrderPlaced") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("PizzaShop.Models.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("OrderDetails"); + }); + + modelBuilder.Entity("PizzaShop.Models.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(6,2)"); + + b.HasKey("Id"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("PizzaShop.Models.Order", b => + { + b.HasOne("PizzaShop.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("PizzaShop.Models.OrderDetail", b => + { + b.HasOne("PizzaShop.Models.Order", "Order") + .WithMany("OrderDetails") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("PizzaShop.Models.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("PizzaShop.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("PizzaShop.Models.Order", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/20251003024533_InitialCreate.cs b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/20251003024533_InitialCreate.cs new file mode 100644 index 0000000..d633d96 --- /dev/null +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/20251003024533_InitialCreate.cs @@ -0,0 +1,124 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace pizza_shop.Migrations +{ + /// + public partial class InitialCreate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Customers", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + FirstName = table.Column(type: "nvarchar(max)", nullable: false), + LastName = table.Column(type: "nvarchar(max)", nullable: false), + Address = table.Column(type: "nvarchar(max)", nullable: true), + Phone = table.Column(type: "nvarchar(max)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Customers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Name = table.Column(type: "nvarchar(max)", nullable: false), + Price = table.Column(type: "decimal(6,2)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + OrderPlaced = table.Column(type: "datetime2", nullable: false), + OrderFulfilled = table.Column(type: "datetime2", nullable: true), + CustomerId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + table.ForeignKey( + name: "FK_Orders_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "OrderDetails", + columns: table => new + { + Id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + Quantity = table.Column(type: "int", nullable: false), + ProductId = table.Column(type: "int", nullable: false), + OrderId = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderDetails", x => x.Id); + table.ForeignKey( + name: "FK_OrderDetails_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_OrderDetails_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_OrderDetails_OrderId", + table: "OrderDetails", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderDetails_ProductId", + table: "OrderDetails", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_Orders_CustomerId", + table: "Orders", + column: "CustomerId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "OrderDetails"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.DropTable( + name: "Customers"); + } + } +} diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/PizzaShopContextModelSnapshot.cs b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/PizzaShopContextModelSnapshot.cs new file mode 100644 index 0000000..e35f57b --- /dev/null +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Migrations/PizzaShopContextModelSnapshot.cs @@ -0,0 +1,164 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using PizzaShop.Data; + +#nullable disable + +namespace pizza_shop.Migrations +{ + [DbContext(typeof(PizzaShopContext))] + partial class PizzaShopContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("PizzaShop.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("nvarchar(max)"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Phone") + .HasColumnType("nvarchar(max)"); + + b.HasKey("Id"); + + b.ToTable("Customers"); + }); + + modelBuilder.Entity("PizzaShop.Models.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CustomerId") + .HasColumnType("int"); + + b.Property("OrderFulfilled") + .HasColumnType("datetime2"); + + b.Property("OrderPlaced") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Orders"); + }); + + modelBuilder.Entity("PizzaShop.Models.OrderDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("OrderId") + .HasColumnType("int"); + + b.Property("ProductId") + .HasColumnType("int"); + + b.Property("Quantity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.HasIndex("ProductId"); + + b.ToTable("OrderDetails"); + }); + + modelBuilder.Entity("PizzaShop.Models.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Price") + .HasColumnType("decimal(6,2)"); + + b.HasKey("Id"); + + b.ToTable("Products"); + }); + + modelBuilder.Entity("PizzaShop.Models.Order", b => + { + b.HasOne("PizzaShop.Models.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("PizzaShop.Models.OrderDetail", b => + { + b.HasOne("PizzaShop.Models.Order", "Order") + .WithMany("OrderDetails") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("PizzaShop.Models.Product", "Product") + .WithMany() + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("PizzaShop.Models.Customer", b => + { + b.Navigation("Orders"); + }); + + modelBuilder.Entity("PizzaShop.Models.Order", b => + { + b.Navigation("OrderDetails"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Program.cs b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Program.cs index 694fa2b..4670448 100644 --- a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Program.cs +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/Program.cs @@ -1,2 +1,2 @@ // See https://aka.ms/new-console-template for more information -Console.WriteLine("Graet Pizza Coming Soon"); +Console.WriteLine("Great Pizza Coming Soon"); diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/pizza_shop.csproj b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/pizza_shop.csproj index 96d3725..53140b6 100644 --- a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/pizza_shop.csproj +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/pizza_shop.csproj @@ -9,6 +9,14 @@ + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + \ No newline at end of file diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Data/PizzaShopContext.cs b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Data/PizzaShopContext.cs index 5991623..e92ef2e 100644 --- a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Data/PizzaShopContext.cs +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Data/PizzaShopContext.cs @@ -3,6 +3,10 @@ namespace PizzaShop.Data; +/* + This is the critical piece that Entity Framework uses to determine + the migrations to create. +*/ public class PizzaShopContext : DbContext { public DbSet Customers { get; set; } = null!; @@ -15,7 +19,7 @@ public class PizzaShopContext : DbContext protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { - optionsBuilder.UseSqlServer(); + // Never hard code in PROD of-course + optionsBuilder.UseSqlServer("Server=localhost,1433; Database=PizzaShop; User Id=sa; Password=example_123; Encrypt=false; TrustServerCertificate=true; MultipleActiveResultSets=true;"); } - } \ No newline at end of file diff --git a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Models/OrderDetail.cs b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Models/OrderDetail.cs index 691b0e6..961e529 100644 --- a/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Models/OrderDetail.cs +++ b/ecosystem/database/4_experiments/4_applications/entity_framework_core/pizza_shop/pizza_shop/src/Models/OrderDetail.cs @@ -26,7 +26,7 @@ It will be created as a shadow property. This is a navigation property. It will generate a One-to-Many ER Model. */ - public OrderDetail Order { get; set; } = null!; + public Order Order { get; set; } = null!; /* This is a navigation property. diff --git a/setup.sh b/setup.sh index be5b5bb..bf08536 100755 --- a/setup.sh +++ b/setup.sh @@ -1,17 +1,17 @@ VERSION=8.0.413 -echo "adding asdf dotnet-core plugin" +echo "adding asdf .NET Core plugin" asdf plugin add dotnet-core echo "done" -echo "installing dotnet-core " +echo "Installing .NET Core SDK" asdf install dotnet-core $VERSION echo "done" -echo "set version" - asdf set dotnet-core $VERSION +echo "Installing nuget" + brew install nuget echo "done" -echo "install nuget" - brew install nuget +echo "Installing .NET runtime" + brew install dotnet-runtime echo "done" \ No newline at end of file