Skip to content

Commit 4072589

Browse files
chadgreenCopilot
andcommitted
Add EF Core temporal tables demo (Demo 2)
Build EFCoreDemoFast .NET 8 console app demonstrating EF Core 8 temporal table support against Azure SQL: - TemporalContext with IsTemporal() configuration - EF migration generating temporal table + history table - Program.cs with all 5 temporal LINQ extensions - TemporalAll, TemporalAsOf, TemporalBetween, TemporalFromTo, TemporalContainedIn - Uses TemporalEFDemo database (same server as Demo 1) - Biff-approved ✅ Also: Terraform adds TemporalEFDemo database resource Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 40c8ba0 commit 4072589

22 files changed

Lines changed: 927 additions & 10 deletions

.squad/agents/biff/history.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,23 @@
6868
**Key Insight:** Doc's fixes were **surgical and precise** — they changed only what was broken, didn't refactor unnecessarily. Every fix matched my recommended solution exactly. This is ideal remediation.
6969

7070
**Process Win:** The coordinator clarified that the Employee domain choice was actually Doc's decision as team lead, not a rogue deviation by Marty. This saved needless refactoring back to Product Pricing. Good escalation practice.
71+
72+
### 2026-03-02: EFCoreDemoFast Review — APPROVED ✅
73+
74+
**What I Reviewed:** Jennifer's .NET 8 EF Core demo project (`Demos/EFCoreDemoFast`) for correctness, demo quality, and readiness.
75+
76+
**Review Verdict:****APPROVED** — High-quality, correct, demo-ready.
77+
78+
**Key Findings:**
79+
-**Correctness:** Temporal configuration (`IsTemporal()`) correctly maps to migration and history table
80+
-**Idempotency:** `ExecuteDeleteAsync()` + `MigrateAsync()` ensure repeatable runs
81+
-**Timing:** seedTime captured after SaveChangesAsync; 3-second delay ensures safe TemporalAsOf query window
82+
-**Temporal Queries:** All 5 extensions (TemporalAll, TemporalAsOf, TemporalBetween, TemporalFromTo, TemporalContainedIn) correctly implemented
83+
-**Demo Quality:** Console output formatting is audience-friendly; code comments link EF Core LINQ to Demo 1's T-SQL equivalents; runtime ~7–10 seconds
84+
-**Configuration:** Targets .NET 8; EF Core 8 SQL Server package correct; secrets properly gitignored with appsettings.example.json template
85+
86+
**Minor Suggestion (Non-Blocking):** TemporalBetween and TemporalFromTo may return identical results due to "safe" timestamp boundaries outside transaction windows. Acceptable for the demo—showcasing both API methods is valuable for audience.
87+
88+
**Status:** Ready for Chad to present.
89+
90+
**Process Note:** This demo demonstrates excellent complementarity with Demo 1 (SQL)—same Employee domain narrative, both leverage Azure SQL temporal tables, all query patterns mapped between T-SQL and EF Core LINQ.

.squad/agents/jennifer/history.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,49 @@ modelBuilder.Entity<MyEntity>().ToTable("MyTable", t => t.IsTemporal());
3131
- Supports: creating tables via migrations, converting existing tables, querying history, restoring data
3232

3333
## Learnings
34+
35+
## EFCoreDemoFast — Completed
36+
37+
**Date:** 2026-03-02
38+
**Status:** ✅ Built and compiles successfully
39+
40+
### Key File Paths
41+
- `Demos/EFCoreDemoFast/EFCoreDemoFast.csproj` — .NET 8 console app, EF Core 8 SQL Server
42+
- `Demos/EFCoreDemoFast/Program.cs` — main demo flow with all 5 temporal queries
43+
- `Demos/EFCoreDemoFast/Models/Employee.cs` — POCO, no period columns
44+
- `Demos/EFCoreDemoFast/Data/TemporalContext.cs` — DbContext with `IsTemporal()` config
45+
- `Demos/EFCoreDemoFast/Migrations/20240101000000_InitialCreate.cs` — temporal migration
46+
- `Demos/EFCoreDemoFast/Migrations/TemporalContextModelSnapshot.cs` — EF model snapshot
47+
- `Demos/EFCoreDemoFast/appsettings.json` — gitignored, holds real connection string
48+
- `Demos/EFCoreDemoFast/appsettings.example.json` — safe placeholder for source control
49+
- `Demos/EFCoreDemoFast/README.md` — presenter notes with 2-minute script
50+
51+
### Architecture Decisions
52+
1. **No period columns on POCO** — EF manages PeriodStart/PeriodEnd as shadow properties; `EF.Property<DateTime>(e, "PeriodStart")` used in projections
53+
2. **C# DateTime capture** — seedTime and afterChanges captured as C# variables, not hardcoded SQL timestamps; demonstrates natural C# developer workflow
54+
3. **TemporalAsOf no-projection rule** — EF Core limitation: period columns not available in TemporalAsOf projections; only entity properties selected
55+
4. **Manual migration file** — used fake timestamp `20240101000000` in filename; valid for EF migration ordering
56+
5. **IsTemporal() fluent config** — single line in OnModelCreating; history table name defaults to `EmployeesHistory`
57+
6. **MigrateAsync() at startup** — idempotent, allows `dotnet run` to self-setup
58+
7. **ExecuteDeleteAsync() before seed** — ensures idempotent demo runs
59+
60+
### Patterns Used
61+
- `entity.ToTable(tb => tb.IsTemporal())` — temporal table marker
62+
- `EF.Property<DateTime>(e, "PeriodStart")` — shadow property access in LINQ
63+
- `context.Employees.TemporalAll() / TemporalAsOf() / TemporalBetween() / TemporalFromTo() / TemporalContainedIn()` — all 5 temporal extensions demonstrated
64+
- `await context.Database.MigrateAsync()` — apply migrations at startup
65+
66+
## EFCoreDemoFast — Biff Review Result
67+
68+
**Date:** 2026-03-02
69+
**Reviewer:** Biff
70+
**Status:** ✅ APPROVED
71+
72+
### Approval Feedback
73+
- Temporal configuration and migration are correct
74+
- Idempotency and timing logic are sound
75+
- All 5 temporal LINQ extensions correctly demonstrate Azure SQL temporal table support
76+
- Demo quality suitable for 8-minute conference presentation
77+
- Code comments effectively bridge EF Core to Demo 1's T-SQL patterns
78+
79+
**Next Step:** Ready for Chad's presentation—Demo 2 (EF Core temporal) complements Demo 1 (SQL temporal) with identical Employee narrative across both platforms.

.squad/agents/marty/history.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,23 @@ Create a focused SSMS demo that:
111111
- **Comments matter in live demos**: These aren't just code comments, they're presenter notes and audience reading material
112112

113113
**Decisions Written To:** `.squad/decisions/inbox/marty-sqldemofast-decisions.md`
114+
115+
## TemporalEFDemo Database Resource — Terraform Update
116+
117+
**Date:** 2026-03-02
118+
**Task:** Add TemporalEFDemo database to Terraform for Demo 2 (EF Core)
119+
120+
### Changes Made
121+
- **main.tf:** Added `azurerm_mssql_database` resource for TemporalEFDemo
122+
- Same Azure SQL Server as TemporalSQLDemo (Demo 1)
123+
- Consistent SKU and configuration
124+
- Follows naming and variable conventions
125+
- **outputs.tf:** Added `temporal_ef_demo_db_name` and `temporal_ef_demo_connection_string` outputs
126+
127+
### Verification
128+
✅ Terraform configuration valid
129+
✅ Database resource correctly integrated with existing server infrastructure
130+
✅ Connection strings available for Jennifer's EF Core app configuration
131+
132+
### Architecture Note
133+
Same-server strategy confirmed by Chad—both Demo 1 (SQL) and Demo 2 (EF Core) run independently on single Azure SQL server with separate databases (TemporalSQLDemo and TemporalEFDemo).

.squad/agents/scribe/history.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,27 @@
44
**Project:** Time Travelling Data — 20-minute conference presentation on SQL Server temporal tables
55
**User:** Chad Green
66

7+
## Session: EF Core Demo Build (2026-03-02)
8+
9+
**Team:** Jennifer (EF Core dev) + Marty (Infrastructure) + Biff (Reviewer)
10+
11+
### Orchestration Summary
12+
1. **Jennifer:** Built EFCoreDemoFast .NET 8 console app with all 5 temporal LINQ extensions
13+
2. **Marty:** Added TemporalEFDemo database resource to Terraform (same server as Demo 1)
14+
3. **Biff:** Reviewed and approved EFCoreDemoFast—high quality, demo-ready
15+
4. **Scribe:** Wrote orchestration logs, session log, merged inbox decisions to decisions.md, updated agent history files
16+
17+
### Key Decisions Documented
18+
- **Database Structure:** TemporalEFDemo separate from TemporalSQLDemo, same Azure SQL server
19+
- **Domain Consistency:** Employee narrative identical to Demo 1 (Alice, Bob, Carol, David)
20+
- **Temporal Extensions:** All 5 LINQ methods implemented with T-SQL equivalents in comments
21+
- **DateTime Handling:** C# variables (seedTime, afterChanges) captured at runtime; no hardcoded timestamps
22+
- **Architecture:** Shadow properties for period columns; POCO remains minimal
23+
24+
### Status
25+
✅ Demo 2 approved and ready for Chad's presentation
26+
✅ All team updates documented in agent history files
27+
✅ Decisions merged and deduplicated
28+
✅ Ready for git commit
29+
730
## Learnings

.squad/decisions.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,3 +1752,82 @@ ValidTo DATETIME2 GENERATED ALWAYS AS ROW END HIDDEN NOT NULL,
17521752
---
17531753

17541754

1755+
1756+
1757+
---
1758+
1759+
# Decision: EF Core Demo Structure
1760+
1761+
**Author:** Jennifer
1762+
**Date:** 2026-03-02
1763+
**Status:** Proposed
1764+
1765+
## Context
1766+
1767+
Demo 2 (EFCoreDemoFast) needed an EF Core 8 project showing temporal table support for the "Time Travelling Data" 20-minute conference presentation.
1768+
1769+
## Decisions Made
1770+
1771+
### 1. Database: TemporalEFDemo (separate from Demo 1)
1772+
Demo 2 uses a **different database** (`TemporalEFDemo`) on the same Azure SQL server as Demo 1 (`TemporalSQLDemo`). No cross-dependency between demos. Each demo is fully self-contained.
1773+
1774+
### 2. Employee Domain — consistent with Demo 1
1775+
Carried over Alice Hart, Bob Chen, Carol Reyes, David Kim with the same starting salaries and titles. Demo narrative is identical (Alice promoted, David terminated) so the audience sees the same story told in two different ways.
1776+
1777+
### 3. C# DateTime capture (no hardcoded timestamps)
1778+
Rather than hardcoding SQL timestamps like Demo 1 does for T-SQL demos, Demo 2 captures `DateTime seedTime` and `DateTime afterChanges` as C# variables during execution. This shows the natural C# developer workflow and avoids the "magic strings" problem in live demos.
1779+
1780+
### 4. All 5 temporal LINQ extensions demonstrated
1781+
- `TemporalAll()``FOR SYSTEM_TIME ALL`
1782+
- `TemporalAsOf()``FOR SYSTEM_TIME AS OF`
1783+
- `TemporalBetween()``FOR SYSTEM_TIME BETWEEN`
1784+
- `TemporalFromTo()``FOR SYSTEM_TIME FROM ... TO`
1785+
- `TemporalContainedIn()``FOR SYSTEM_TIME CONTAINED IN`
1786+
1787+
Each LINQ call maps to its T-SQL equivalent (shown in comments) to bridge the two demos for the audience.
1788+
1789+
### 5. No period columns on POCO
1790+
PeriodStart/PeriodEnd are EF shadow properties only. The Employee POCO is intentionally minimal — demonstrates that temporal behavior is infrastructure, not domain model concern.
1791+
1792+
### 6. Migration hand-crafted with fake timestamp
1793+
The migration file uses `20240101000000` as a timestamp. This is fine — it just needs to be a valid date string for EF ordering. This avoids requiring `dotnet ef migrations add` at setup time.
1794+
1795+
### 7. appsettings.json gitignored
1796+
Connection string lives in `appsettings.json` (gitignored). `appsettings.example.json` committed with placeholder values. Presenter updates `appsettings.json` before the demo.
1797+
1798+
1799+
---
1800+
1801+
# Biff's Review of EF Core Demo (Demo 2)
1802+
1803+
## Review Verdict: APPROVED ✅
1804+
1805+
I have thoroughly reviewed the EF Core demo project (`Demos/EFCoreDemoFast`) and found it to be high-quality, correct, and demo-ready.
1806+
1807+
### 1. Correctness & Logic
1808+
- **Temporal Configuration:** The `IsTemporal()` configuration in `TemporalContext.cs` correctly maps to the `20240101000000_InitialCreate.cs` migration. The history table `EmployeesHistory` is correctly defined.
1809+
- **Idempotency:** `ExecuteDeleteAsync()` at the start of `Program.cs` ensures the demo can be run repeatedly without duplicating data. `MigrateAsync()` ensures the database is created.
1810+
- **Seeding & Timing:**
1811+
- `seedTime` is captured *after* `SaveChangesAsync()`, ensuring the seeded rows have `PeriodStart` timestamps <= `seedTime`.
1812+
- The 3-second delay (`Task.Delay(3000)`) provides a safe buffer ensuring `seedTime.AddSeconds(1)` falls strictly between the seed transaction and the update transaction.
1813+
- `afterChanges` is captured correctly after the second transaction.
1814+
1815+
### 2. Temporal Query Validation
1816+
- **TemporalAsOf:** `TemporalAsOf(seedTime.AddSeconds(1))` will correctly return all 4 original employees (Alice, Bob, Carol, David). The query time (T_seed + 1s) is guaranteed to be before the updates (T_seed + 3s). The expected output in README correctly shows all 4.
1817+
- **TemporalContainedIn:** David's row is created at T_seed and deleted at T_update. The query window `(seedTime - 1s, afterChanges + 1s)` fully encompasses David's valid period `[T_seed, T_update)`. This is correct.
1818+
- **TemporalBetween vs FromTo:** The distinction is subtle but the code uses them correctly. Given the timestamps are derived from execution time, the results will likely be identical in this specific run, but showcasing both API methods is valuable for the audience.
1819+
1820+
### 3. Demo Quality
1821+
- **Readability:** The console output (using `Console.WriteLine` with table formatting) is excellent and audience-friendly.
1822+
- **Code Clarity:** The comments in `Program.cs` effectively link the EF Core LINQ methods to their T-SQL counterparts from Demo 1 (e.g., `// LINQ: ...TemporalAll()`, `// T-SQL: ...FOR SYSTEM_TIME ALL`).
1823+
- **Timing:** The demo is concise. Migration check (~1s) + Seed (~1s) + Wait (3s) + Update (~1s) + Queries (~1s) = ~7-10 seconds runtime. The 2-minute script estimate allows ample time for the speaker to explain the code.
1824+
1825+
### 4. Configuration
1826+
- **Project File:** Correctly targets .NET 8 and uses `Microsoft.EntityFrameworkCore.SqlServer` 8.0.0.
1827+
- **Secrets:** `appsettings.json` is correctly in `.gitignore`. `appsettings.example.json` provides a safe template.
1828+
1829+
### Minor Suggestion (Non-Blocking)
1830+
- The `TemporalBetween` and `TemporalFromTo` queries might return identical results in this specific scenario because the start/end points are "safe" timestamps outside the transaction boundaries. This is acceptable for the demo as it proves the API works, even if it doesn't highlight the edge-case boundary differences (which are hard to demo reliably with live execution timing).
1831+
1832+
**Ready for Chad to present.**
1833+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Orchestration — Biff
2+
3+
**Session:** 2026-03-02T02:14:55Z
4+
**Agent:** Biff
5+
**Status:** Complete ✅
6+
7+
## Mission
8+
Review EFCoreDemoFast .NET 8 console app for correctness, demo quality, and readiness.
9+
10+
## Review Verdict
11+
**APPROVED ✅** — High-quality, correct, demo-ready.
12+
13+
## Key Findings
14+
15+
### Correctness & Logic
16+
- ✅ Temporal configuration (IsTemporal()) correctly maps to migration and history table
17+
- ✅ Idempotency: ExecuteDeleteAsync() + MigrateAsync() ensure repeatable runs
18+
- ✅ Seeding & timing: seedTime captured after SaveChangesAsync; 3-second delay ensures TemporalAsOf window is safe
19+
- ✅ afterChanges captured correctly after second transaction
20+
21+
### Temporal Query Validation
22+
- ✅ TemporalAsOf(seedTime.AddSeconds(1)) returns all 4 original employees (before updates)
23+
- ✅ TemporalContainedIn correctly spans David's entire valid period
24+
- ✅ TemporalBetween vs FromTo both correctly implemented (may return identical results in this scenario, which is acceptable)
25+
26+
### Demo Quality
27+
- ✅ Console output formatting is audience-friendly
28+
- ✅ Code comments effectively link EF Core LINQ to T-SQL equivalents from Demo 1
29+
- ✅ Runtime: ~7–10 seconds + ample time for speaker explanation
30+
31+
### Configuration
32+
- ✅ Project targets .NET 8 with correct EF Core package versions
33+
- ✅ Secrets: appsettings.json gitignored, appsettings.example.json provided
34+
35+
## Minor Suggestion (Non-Blocking)
36+
TemporalBetween and TemporalFromTo may return identical results due to "safe" timestamp boundaries outside transaction windows. Acceptable for the demo.
37+
38+
## Status
39+
**Ready for Chad to present.**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Orchestration — Jennifer
2+
3+
**Session:** 2026-03-02T02:14:55Z
4+
**Agent:** Jennifer
5+
**Status:** Complete ✅
6+
7+
## Mission
8+
Build EFCoreDemoFast .NET 8 console app demonstrating EF Core 8 temporal table support against Azure SQL.
9+
10+
## Deliverables
11+
- `Demos/EFCoreDemoFast/` — Complete .NET 8 console application
12+
- `EFCoreDemoFast.csproj` — project file
13+
- `Program.cs` — main flow with all 5 temporal LINQ extensions
14+
- `Models/Employee.cs` — domain model (POCO, no period columns)
15+
- `Data/TemporalContext.cs` — DbContext with `IsTemporal()` configuration
16+
- `Migrations/20240101000000_InitialCreate.cs` — temporal table migration
17+
- `appsettings.json` — gitignored connection string
18+
- `appsettings.example.json` — template for source control
19+
- `README.md` — 2-minute presenter script and notes
20+
21+
## Key Decisions
22+
1. **Database:** TemporalEFDemo (separate from Demo 1, same Azure SQL server)
23+
2. **Domain:** Employee (Alice Hart, Bob Chen, Carol Reyes, David Kim) — consistent with Demo 1 narrative
24+
3. **C# DateTime Capture:** seedTime and afterChanges captured as C# variables during execution; no hardcoded SQL timestamps
25+
4. **All 5 Temporal Extensions:** TemporalAll, TemporalAsOf, TemporalBetween, TemporalFromTo, TemporalContainedIn — each with T-SQL comment mapping
26+
5. **Shadow Properties:** PeriodStart/PeriodEnd managed by EF; POCO is intentionally minimal
27+
6. **Migration Timestamp:** Fake timestamp `20240101000000` in filename (valid for EF ordering)
28+
7. **Idempotency:** ExecuteDeleteAsync() before seed, MigrateAsync() at startup
29+
30+
## Verification
31+
✅ Project compiles successfully
32+
✅ All files created and structure correct
33+
✅ Ready for Biff review
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Orchestration — Marty
2+
3+
**Session:** 2026-03-02T02:14:55Z
4+
**Agent:** Marty
5+
**Status:** Complete ✅
6+
7+
## Mission
8+
Add TemporalEFDemo database resource to Terraform configuration for Demo 2.
9+
10+
## Deliverables
11+
- `Demos/SQLDemoFast/terraform/main.tf` — Updated with TemporalEFDemo database resource
12+
- `Demos/SQLDemoFast/terraform/outputs.tf` — Updated with TemporalEFDemo connection outputs
13+
14+
## Changes Made
15+
1. **Database Resource:** Added `azurerm_mssql_database` for `TemporalEFDemo`
16+
- Same Azure SQL Server as TemporalSQLDemo (Demo 1)
17+
- Same SKU and configuration for consistency
18+
- Follows naming convention and variable patterns
19+
2. **Outputs:** Added `temporal_ef_demo_db_name` and `temporal_ef_demo_connection_string`
20+
21+
## Verification
22+
✅ Terraform configuration valid
23+
✅ Database resource correctly integrated with existing server
24+
✅ Connection string outputs available for Jennifer's connection setup

Demos/EFCoreDemoFast/.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# appsettings.json contains your real connection string — never commit it
2+
appsettings.json
3+
4+
# Build output
5+
bin/
6+
obj/
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using EFCoreDemoFast.Models;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.Extensions.Configuration;
4+
5+
namespace EFCoreDemoFast.Data;
6+
7+
public class TemporalContext : DbContext
8+
{
9+
public DbSet<Employee> Employees => Set<Employee>();
10+
11+
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
12+
{
13+
var config = new ConfigurationBuilder()
14+
.AddJsonFile("appsettings.json", optional: false)
15+
.AddEnvironmentVariables()
16+
.Build();
17+
18+
optionsBuilder.UseSqlServer(config.GetConnectionString("DefaultConnection"));
19+
}
20+
21+
protected override void OnModelCreating(ModelBuilder modelBuilder)
22+
{
23+
modelBuilder.Entity<Employee>(entity =>
24+
{
25+
entity.Property(e => e.Name).HasMaxLength(100);
26+
entity.Property(e => e.Title).HasMaxLength(100);
27+
entity.Property(e => e.Salary).HasColumnType("decimal(10,2)");
28+
entity.Property(e => e.Department).HasMaxLength(100);
29+
30+
// ── This is the magic line ──────────────────────────────────────
31+
// Tells EF Core to create this as a system-versioned temporal table.
32+
// EF automatically creates the history table and period columns.
33+
entity.ToTable(tb => tb.IsTemporal());
34+
});
35+
}
36+
}

0 commit comments

Comments
 (0)