Add custom SQL to create a view for Teams and Leagues with migration.

Also state that this view is keyless.
This commit is contained in:
Kevin Matsubara 2025-04-07 20:10:53 +02:00
parent 9da28603c1
commit dd195dfddf
5 changed files with 535 additions and 0 deletions

View File

@ -15,6 +15,8 @@ context.Database.EnsureCreated();
// Rather than raise: "System.InvalidOperationException: Sequence contains no elements." // Rather than raise: "System.InvalidOperationException: Sequence contains no elements."
// var firstCoach = await context.Coaches.FirstOrDefaultAsync(); // var firstCoach = await context.Coaches.FirstOrDefaultAsync();
var details = await context.TeamsAndLeaguesView.ToListAsync();
async Task ProjectionAndAnonymousDataTypes() async Task ProjectionAndAnonymousDataTypes()
{ {
var teams = await context.Teams var teams = await context.Teams

View File

@ -20,6 +20,7 @@ public class DeadBallZoneLeagueDbContext : DbContext
public DbSet<Coach> Coaches { get; set; } public DbSet<Coach> Coaches { get; set; }
public DbSet<League> Leagues { get; set; } public DbSet<League> Leagues { get; set; }
public DbSet<Match> Matches { get; set; } public DbSet<Match> Matches { get; set; }
public DbSet<TeamsAndLeaguesView> TeamsAndLeaguesView { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{ {
@ -37,5 +38,8 @@ public class DeadBallZoneLeagueDbContext : DbContext
// This line can be used, then individual configurations do not need to be loaded. // This line can be used, then individual configurations do not need to be loaded.
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
// This database object does not have a Primary Key, so we state that here, otherwise an exception is thrown.
modelBuilder.Entity<TeamsAndLeaguesView>().HasNoKey().ToView("vw_TeamsAndLeagues");
} }
} }

View File

@ -0,0 +1,490 @@
// <auto-generated />
using System;
using EntityFrameworkCore.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
#nullable disable
namespace EntityFrameworkCore.Data.Migrations
{
[DbContext(typeof(DeadBallZoneLeagueDbContext))]
[Migration("20250407174459_AddTeamLeaguesAndCoachesView")]
partial class AddTeamLeaguesAndCoachesView
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.3");
modelBuilder.Entity("EntityFrameworkCore.Domain.Coach", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedDate")
.HasColumnType("TEXT");
b.Property<string>("ModifiedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("ModifiedDate")
.HasColumnType("TEXT");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Coaches");
b.HasData(
new
{
Id = 1,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Christian Southgate"
},
new
{
Id = 2,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Rob Mann"
},
new
{
Id = 3,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Jon Curtis"
},
new
{
Id = 4,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Andy Taylor"
},
new
{
Id = 5,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Steve Johnson"
},
new
{
Id = 6,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Dan Cook"
},
new
{
Id = 7,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Ken Jarvis"
},
new
{
Id = 8,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Kenny Suzuki"
},
new
{
Id = 9,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Gordon Hall"
},
new
{
Id = 10,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "John O Dowd"
},
new
{
Id = 11,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Julian Widdows"
},
new
{
Id = 12,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Andy Williams"
},
new
{
Id = 13,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Trevor Williams"
},
new
{
Id = 14,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Blake Deathray"
},
new
{
Id = 15,
CreatedDate = new DateTime(2025, 4, 7, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Rock Housebrick"
});
});
modelBuilder.Entity("EntityFrameworkCore.Domain.League", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedDate")
.HasColumnType("TEXT");
b.Property<string>("ModifiedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("ModifiedDate")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("Leagues");
b.HasData(
new
{
Id = 1,
CreatedDate = new DateTime(2025, 4, 6, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Local League"
},
new
{
Id = 2,
CreatedDate = new DateTime(2025, 4, 6, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "National League"
},
new
{
Id = 3,
CreatedDate = new DateTime(2025, 4, 6, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Geosphere"
},
new
{
Id = 4,
CreatedDate = new DateTime(2025, 4, 6, 17, 7, 27, 33, DateTimeKind.Unspecified),
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Cyber war"
});
});
modelBuilder.Entity("EntityFrameworkCore.Domain.Match", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("AwayTeamId")
.HasColumnType("INTEGER");
b.Property<int>("AwayTeamScore")
.HasColumnType("INTEGER");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedDate")
.HasColumnType("TEXT");
b.Property<DateTime>("Date")
.HasColumnType("TEXT");
b.Property<int>("HomeTeamId")
.HasColumnType("INTEGER");
b.Property<int>("HomeTeamScore")
.HasColumnType("INTEGER");
b.Property<string>("ModifiedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("ModifiedDate")
.HasColumnType("TEXT");
b.Property<decimal>("TicketPrice")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("AwayTeamId");
b.HasIndex("HomeTeamId");
b.ToTable("Matches");
});
modelBuilder.Entity("EntityFrameworkCore.Domain.Team", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<int>("CoachId")
.HasColumnType("INTEGER");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("CreatedDate")
.HasColumnType("TEXT");
b.Property<int?>("LeagueId")
.HasColumnType("INTEGER");
b.Property<string>("ModifiedBy")
.HasColumnType("TEXT");
b.Property<DateTime>("ModifiedDate")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("CoachId")
.IsUnique();
b.HasIndex("LeagueId");
b.HasIndex("Name")
.IsUnique();
b.ToTable("Teams");
b.HasData(
new
{
Id = 1,
CoachId = 1,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 2,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Neo Delhi"
},
new
{
Id = 2,
CoachId = 2,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 1,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Voodoo"
},
new
{
Id = 3,
CoachId = 3,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 1,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Penal X"
},
new
{
Id = 4,
CoachId = 4,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 3,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Neo Tokyo"
},
new
{
Id = 5,
CoachId = 5,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 2,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Neo Barcelona"
},
new
{
Id = 6,
CoachId = 6,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 2,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Neo Manchester"
},
new
{
Id = 7,
CoachId = 7,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 3,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Neo Bangkok"
},
new
{
Id = 8,
CoachId = 8,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 3,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Neo Amsterdam"
},
new
{
Id = 9,
CoachId = 9,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 1,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Killaklowns"
},
new
{
Id = 10,
CoachId = 10,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 1,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Sol"
},
new
{
Id = 11,
CoachId = 11,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 4,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "DEC"
},
new
{
Id = 12,
CoachId = 12,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 1,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Leopards"
},
new
{
Id = 13,
CoachId = 13,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 1,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Harlequins"
},
new
{
Id = 14,
CoachId = 14,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 4,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Gladiators"
},
new
{
Id = 15,
CoachId = 15,
CreatedDate = new DateTime(2025, 4, 4, 17, 7, 27, 33, DateTimeKind.Unspecified),
LeagueId = 1,
ModifiedDate = new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified),
Name = "Fiz-O"
});
});
modelBuilder.Entity("EntityFrameworkCore.Domain.Match", b =>
{
b.HasOne("EntityFrameworkCore.Domain.Team", "AwayTeam")
.WithMany("AwayMatches")
.HasForeignKey("AwayTeamId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("EntityFrameworkCore.Domain.Team", "HomeTeam")
.WithMany("HomeMatches")
.HasForeignKey("HomeTeamId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("AwayTeam");
b.Navigation("HomeTeam");
});
modelBuilder.Entity("EntityFrameworkCore.Domain.Team", b =>
{
b.HasOne("EntityFrameworkCore.Domain.Coach", "Coach")
.WithOne("Team")
.HasForeignKey("EntityFrameworkCore.Domain.Team", "CoachId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("EntityFrameworkCore.Domain.League", "League")
.WithMany("Teams")
.HasForeignKey("LeagueId");
b.Navigation("Coach");
b.Navigation("League");
});
modelBuilder.Entity("EntityFrameworkCore.Domain.Coach", b =>
{
b.Navigation("Team");
});
modelBuilder.Entity("EntityFrameworkCore.Domain.League", b =>
{
b.Navigation("Teams");
});
modelBuilder.Entity("EntityFrameworkCore.Domain.Team", b =>
{
b.Navigation("AwayMatches");
b.Navigation("HomeMatches");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,32 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace EntityFrameworkCore.Data.Migrations
{
/// <inheritdoc />
public partial class AddTeamLeaguesAndCoachesView : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
// I added this SQL manually, by first creating an empty migration.
// A view is a read-only representation of a data set.
migrationBuilder.Sql(@"
CREATE VIEW vw_TeamsAndLeagues
AS
SELECT t.name, l.Name AS LeagueName
FROM Teams AS t
LEFT JOIN Leagues AS l ON t.LeagueId = l.Id
");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
// Don't forget this part as well, for each manually added SQL statement.
migrationBuilder.Sql(@"DROP VIEW vw_TeamsAndLeagues");
}
}
}

View File

@ -0,0 +1,7 @@
namespace EntityFrameworkCore.Domain;
public class TeamsAndLeaguesView
{
public string? Name { get; set; }
public string? LeagueName { get; set; }
}