528 lines
13 KiB
C#
528 lines
13 KiB
C#
using EntityFrameworkCore.Data;
|
|
using EntityFrameworkCore.Domain;
|
|
using Microsoft.Data.Sqlite;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
using var context = new DeadBallZoneLeagueDbContext();
|
|
// context.Database.Migrate(); // Can be used to automatically migrate on run.
|
|
context.Database.EnsureCreated();
|
|
|
|
// var teamOne = await context.Teams.FirstAsync(t => t.Id == 1);
|
|
// var teamTwo = await context.Teams.FirstAsync(t => t.Id == 2);
|
|
// var teamThree = await context.Teams.SingleAsync(t => t.Id == 3);
|
|
// var teamFour = await context.Teams.SingleOrDefaultAsync(t => t.Id == 3);
|
|
|
|
// There is no entry for coaches yet, this function will provide a null to coach.
|
|
// Rather than raise: "System.InvalidOperationException: Sequence contains no elements."
|
|
// var firstCoach = await context.Coaches.FirstOrDefaultAsync();
|
|
|
|
|
|
|
|
async Task QueryScalarType()
|
|
{
|
|
// Query a scalar or non-entity type.
|
|
// Scalar subqueries or scalar queries are queries that return exactly one column and one or zero records.
|
|
// Source: https://stackoverflow.com/questions/20424966/what-is-a-scalar-query
|
|
var leagueIds = context.Database.SqlQuery<int>(
|
|
$"SELECT Id FROM Leagues"
|
|
).ToList();
|
|
}
|
|
|
|
async Task RawSQLStatement()
|
|
{
|
|
// Beware that SQL Injection can be used on input here.
|
|
Console.WriteLine("Enter Team name: ");
|
|
var teamName = Console.ReadLine();
|
|
// If you omit this param creation, SQL injection is possible.
|
|
var teamNameParam = new SqliteParameter("teamName", teamName);
|
|
var teams = context.Teams.FromSqlRaw(
|
|
$"SELECT * FROM Teams WHERE name = @teamName", teamNameParam
|
|
);
|
|
foreach (var t in teams)
|
|
{
|
|
Console.WriteLine($"SQL Raw Team: {t.Name}");
|
|
}
|
|
|
|
// Parameterization happens automatically for these two:
|
|
teams = context.Teams.FromSql($"SELECT * FROM Teams WHERE name = {teamName}");
|
|
foreach (var t in teams)
|
|
{
|
|
Console.WriteLine($"SQL Raw Team: {t.Name}");
|
|
}
|
|
|
|
// FromSqlInterpolated is the revised form of FromSql, and a better choice.
|
|
teams = context.Teams.FromSqlInterpolated($"SELECT * FROM Teams WHERE name = {teamName}");
|
|
foreach (var t in teams)
|
|
{
|
|
Console.WriteLine($"SQL Raw Team: {t.Name}");
|
|
}
|
|
|
|
// You can also mix with LINQ.
|
|
var teamsList = context.Teams.FromSql($"SELECT * FROM Teams")
|
|
.Where(t => t.Id == 1)
|
|
.OrderBy(t => t.Id)
|
|
.Include("League")
|
|
.ToList();
|
|
foreach (var t in teamsList)
|
|
{
|
|
Console.WriteLine($"SQL Raw Team: {t.Name}");
|
|
}
|
|
|
|
// Stored procedures example. Note that SQLite does not support stored proceduces.
|
|
var leagueId = 1;
|
|
var league = context.Leagues.FromSqlInterpolated(
|
|
$"EXEC dbo.StoredProcedureToGetLeagueName {leagueId}"
|
|
);
|
|
|
|
// Non-querying statement examples.
|
|
var newTeamName = "Neo Oslo";
|
|
var success = context.Database.ExecuteSqlInterpolated(
|
|
$"UPDATE Teams SET Name = {newTeamName}"
|
|
);
|
|
|
|
var teamToDelete = 1;
|
|
var teamDeletedSuccess = context.Database.ExecuteSqlInterpolated(
|
|
$"EXEC dbo.DeleteTeam {teamToDelete}"
|
|
);
|
|
}
|
|
|
|
async Task ProjectionAndAnonymousDataTypes()
|
|
{
|
|
var teams = await context.Teams
|
|
.Select(t => new TeamDetailsDTO
|
|
{
|
|
TeamId = t.Id,
|
|
TeamName = t.Name,
|
|
CoachName = t.Coach.Name,
|
|
TotalHomeGoals = t.HomeMatches.Sum(m => m.HomeTeamScore),
|
|
TotalAwayGoals = t.AwayMatches.Sum(m => m.AwayTeamScore)
|
|
})
|
|
.ToListAsync();
|
|
|
|
foreach (var team in teams)
|
|
{
|
|
Console.WriteLine($"{team.TeamName} - {team.CoachName} | H: {team.TotalHomeGoals} | A: {team.TotalAwayGoals}");
|
|
}
|
|
}
|
|
|
|
async Task FilterScoredHomeMatches()
|
|
{
|
|
// Get teams where they scored on home matches.
|
|
var teams = await context.Teams
|
|
.Include(t => t.Coach)
|
|
.Include(t => t.HomeMatches.Where(m => m.HomeTeamScore > 0))
|
|
.ToListAsync();
|
|
foreach (var team in teams)
|
|
{
|
|
Console.WriteLine($"{team.Name} - {team.Coach.Name}");
|
|
foreach (var match in team.HomeMatches)
|
|
{
|
|
Console.WriteLine($"Score: {match.HomeTeamScore}");
|
|
}
|
|
}
|
|
}
|
|
|
|
async Task InsertMatches()
|
|
{
|
|
var match1 = new Match
|
|
{
|
|
AwayTeamId = 10,
|
|
HomeTeamId = 12,
|
|
HomeTeamScore = 1,
|
|
AwayTeamScore = 0,
|
|
Date = new DateTime(2025, 6, 10),
|
|
TicketPrice = 20
|
|
};
|
|
var match2 = new Match
|
|
{
|
|
AwayTeamId = 6,
|
|
HomeTeamId = 9,
|
|
HomeTeamScore = 1,
|
|
AwayTeamScore = 0,
|
|
Date = new DateTime(2025, 6, 11),
|
|
TicketPrice = 20
|
|
};
|
|
var match3 = new Match
|
|
{
|
|
AwayTeamId = 13,
|
|
HomeTeamId = 15,
|
|
HomeTeamScore = 1,
|
|
AwayTeamScore = 0,
|
|
Date = new DateTime(2025, 6, 12),
|
|
TicketPrice = 20
|
|
};
|
|
var match4 = new Match
|
|
{
|
|
AwayTeamId = 4,
|
|
HomeTeamId = 11,
|
|
HomeTeamScore = 0,
|
|
AwayTeamScore = 1,
|
|
Date = new DateTime(2025, 6, 12),
|
|
TicketPrice = 20
|
|
};
|
|
|
|
await context.AddRangeAsync(match1, match2, match3, match4);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
|
|
async Task ExplicitLoadingExample()
|
|
{
|
|
var league = await context.FindAsync<League>(1);
|
|
if (!league.Teams.Any())
|
|
{
|
|
Console.WriteLine("Teams have not been loaded.");
|
|
}
|
|
|
|
await context.Entry(league)
|
|
.Collection(l => l.Teams)
|
|
.LoadAsync();
|
|
|
|
if (league.Teams.Any())
|
|
{
|
|
foreach (var team in league.Teams)
|
|
{
|
|
Console.WriteLine($"{team.Name}");
|
|
}
|
|
}
|
|
}
|
|
|
|
async Task EagerLoadingExample()
|
|
{
|
|
var leagues = await context.Leagues
|
|
.Include(l => l.Teams)
|
|
.ThenInclude(t => t.Coach)
|
|
.ToListAsync();
|
|
|
|
foreach (var league in leagues)
|
|
{
|
|
Console.WriteLine($"League - {league.Name}");
|
|
foreach (var team in league.Teams)
|
|
{
|
|
Console.WriteLine($"* {team.Name} - {team.Coach.Name}");
|
|
}
|
|
}
|
|
}
|
|
|
|
async Task InsertRelatedData()
|
|
{
|
|
// Insert record with a Foreign Key.
|
|
var match = new Match
|
|
{
|
|
AwayTeamId = 1,
|
|
HomeTeamId = 2,
|
|
AwayTeamScore = 0,
|
|
HomeTeamScore = 0,
|
|
Date = new DateTime(2025, 6, 1),
|
|
TicketPrice = 20
|
|
};
|
|
|
|
await context.AddAsync(match);
|
|
await context.SaveChangesAsync();
|
|
|
|
// Insert Parent/Child
|
|
var team = new Team
|
|
{
|
|
Name = "Neo Istanbul",
|
|
Coach = new Coach
|
|
{
|
|
Name = "Gimbal Wizbouski"
|
|
}
|
|
};
|
|
|
|
await context.AddAsync(team);
|
|
await context.SaveChangesAsync();
|
|
|
|
// Insert Parent with Children.
|
|
var league = new League
|
|
{
|
|
Name = "Star League",
|
|
Teams = new List<Team>
|
|
{
|
|
new Team
|
|
{
|
|
Name = "Neo Cairo",
|
|
Coach = new Coach
|
|
{
|
|
Name = "Damon Pulsar"
|
|
}
|
|
},
|
|
new Team
|
|
{
|
|
Name = "Cyborgs",
|
|
Coach = new Coach
|
|
{
|
|
Name = "C-1"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
await context.AddAsync(league);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
|
|
async Task ExecuteDelete(string name)
|
|
{
|
|
// var coaches = await context.Coaches.Where(c => c.Name == name).ToListAsync();
|
|
// context.RemoveRange(coaches);
|
|
// await context.SaveChangesAsync();
|
|
|
|
// Simplified:
|
|
await context.Coaches.Where(c => c.Name == name).ExecuteDeleteAsync();
|
|
}
|
|
|
|
async Task ExecuteUpdate(int id)
|
|
{
|
|
await context.Coaches
|
|
.Where(c => c.Id == id)
|
|
.ExecuteUpdateAsync(set => set.SetProperty(c => c.CreatedDate, DateTime.UtcNow));
|
|
}
|
|
|
|
async Task DeleteCoach(int id)
|
|
{
|
|
var coach = await context.Coaches.FindAsync(id);
|
|
if (coach != null)
|
|
{
|
|
context.Remove(coach);
|
|
// context.Entry(coach).State = EntityState.Deleted;
|
|
await context.SaveChangesAsync();
|
|
}
|
|
}
|
|
|
|
async Task UpdateCoach()
|
|
{
|
|
var coach = await context.Coaches.FindAsync(1);
|
|
coach.CreatedDate = DateTime.UtcNow;
|
|
await context.SaveChangesAsync();
|
|
}
|
|
|
|
async Task UpdateCoachNoTracking()
|
|
{
|
|
var coach = await context.Coaches
|
|
.AsNoTracking()
|
|
.FirstOrDefaultAsync(c => c.Id == 1);
|
|
// .FindAsync(1); // cannot be used when using no-tracking.
|
|
coach.CreatedDate = DateTime.UtcNow;
|
|
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
|
|
context.Update(coach); // Set Entity state to be updated.
|
|
// Note that also other fields are considered "updated/modified", such as the CreatedDate.
|
|
// You can also use:
|
|
// context.Entry(coach).State = EntityState.Modified;
|
|
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
|
|
await context.SaveChangesAsync();
|
|
}
|
|
|
|
async Task InsertCoach(string name)
|
|
{
|
|
var coach = new Coach { Name = name, CreatedDate = DateTime.UtcNow };
|
|
await context.Coaches.AddAsync(coach);
|
|
Console.WriteLine(context.ChangeTracker.DebugView.LongView);
|
|
await context.SaveChangesAsync();
|
|
|
|
// If you have a list, you can use a batch insert, instead of a loop:
|
|
// await context.Coaches.AddRangeAsync(coaches);
|
|
}
|
|
|
|
async Task ListVSIQueryableCall()
|
|
{
|
|
Console.WriteLine("Enter '1' for team with Id 1 or enter '2' for teams that contain 'Neo'");
|
|
var option = Convert.ToInt32(Console.ReadLine());
|
|
List<Team> teamsAsList = new List<Team>();
|
|
// After executing ToList, the records are stored in memory.
|
|
// Operations after that, are also done in memory.
|
|
|
|
teamsAsList = await context.Teams.ToListAsync();
|
|
if (option == 1)
|
|
{
|
|
teamsAsList = teamsAsList.Where(t => t.Id == 1).ToList();
|
|
}
|
|
else if (option == 2)
|
|
{
|
|
teamsAsList = teamsAsList.Where(t => t.Name.Contains("Neo")).ToList();
|
|
}
|
|
foreach (var team in teamsAsList)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
|
|
// Records stay as IQueryable until the ToList function is called, then the final query is performed.
|
|
var teamsAsQueryable = context.Teams.AsQueryable();
|
|
if (option == 1)
|
|
{
|
|
teamsAsQueryable = teamsAsQueryable.Where(t => t.Id == 1);
|
|
}
|
|
else if (option == 2)
|
|
{
|
|
teamsAsQueryable = teamsAsQueryable.Where(t => t.Name.Contains("Neo"));
|
|
}
|
|
|
|
// the actual query:
|
|
teamsAsList = await teamsAsQueryable.ToListAsync();
|
|
|
|
foreach (var team in teamsAsList)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
}
|
|
|
|
async Task TrackedQuery()
|
|
{
|
|
// EF Core tracks objects that are returned by queries.
|
|
// This is less useful in disconnected applictations like APIs and Web applications.
|
|
var teams = await context.Teams
|
|
.AsNoTracking()
|
|
.ToListAsync();
|
|
|
|
foreach (var team in teams)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
}
|
|
|
|
async Task SkipAndTakeTeams()
|
|
{
|
|
var recordCount = 3;
|
|
var page = 0;
|
|
var next = true;
|
|
while (next)
|
|
{
|
|
var teams = await context.Teams.Skip(page * recordCount).Take(recordCount).ToListAsync();
|
|
foreach (var team in teams)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
Console.WriteLine("Enter 'true' for next set of records, or 'false to quit.'");
|
|
next = Convert.ToBoolean(Console.ReadLine());
|
|
|
|
if (!next) break;
|
|
page += 1;
|
|
}
|
|
}
|
|
|
|
async Task OrderedTeams()
|
|
{
|
|
var orderedTeam = await context.Teams
|
|
.OrderByDescending(t => t.Name)
|
|
.ToListAsync();
|
|
foreach (var team in orderedTeam)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
}
|
|
|
|
async Task GroupedTeams()
|
|
{
|
|
var groupedTeams = context.Teams
|
|
.GroupBy(t => t.CreatedDate.Date);
|
|
|
|
foreach (var groupedTeam in groupedTeams)
|
|
{
|
|
Console.WriteLine(groupedTeam.Key);
|
|
Console.WriteLine(groupedTeam.Sum(t => t.Id));
|
|
foreach (var team in groupedTeam)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
async Task CountFunctionsTeams(int id)
|
|
{
|
|
var numberOfTeams = await context.Teams.CountAsync(t => t.Id > id);
|
|
Console.WriteLine($"Number of teams with ID > {id}: {numberOfTeams}");
|
|
|
|
var maxTeams = await context.Teams.MaxAsync(t => t.Id);
|
|
Console.WriteLine($"Max teams: {maxTeams}");
|
|
|
|
var minTeams = await context.Teams.MinAsync(t => t.Id);
|
|
Console.WriteLine($"Min teams: {minTeams}");
|
|
|
|
var averageTeams = await context.Teams.AverageAsync(t => t.Id);
|
|
Console.WriteLine($"Average teams: {averageTeams}");
|
|
|
|
var sumTeams = await context.Teams.SumAsync(t => t.Id);
|
|
Console.WriteLine($"Sum team IDs: {sumTeams}");
|
|
}
|
|
|
|
async Task GetAllTeamsQuerySyntax(string searchTerm)
|
|
{
|
|
var teams = await (
|
|
from team in context.Teams
|
|
where EF.Functions.Like(team.Name, $"%{searchTerm}%")
|
|
select team)
|
|
.ToListAsync();
|
|
foreach (var team in teams)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
}
|
|
|
|
async Task PrintTeams()
|
|
{
|
|
var teams = await context.Teams.ToListAsync();
|
|
|
|
foreach (var team in teams)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
}
|
|
|
|
async Task PrintFilteredTeams()
|
|
{
|
|
Console.WriteLine("Enter search term: ");
|
|
var searchTerm = Console.ReadLine();
|
|
|
|
// var filteredTeams = await context.Teams.Where(t => t.Name.Contains(searchTerm)).ToListAsync();
|
|
var filteredTeams = await context.Teams.Where(t => EF.Functions.Like(t.Name, $"%{searchTerm}%")).ToListAsync();
|
|
foreach (var team in filteredTeams)
|
|
{
|
|
Console.WriteLine(team.Name);
|
|
}
|
|
}
|
|
|
|
async Task PrintTeamById(int id)
|
|
{
|
|
// Selection based on finding the Primary Key.
|
|
var teamBasedOnId = await context.Teams.FindAsync(id);
|
|
if (teamBasedOnId != null)
|
|
{
|
|
Console.WriteLine(teamBasedOnId.Name);
|
|
}
|
|
}
|
|
|
|
async Task SelectQuery()
|
|
{
|
|
var items = await context.Teams
|
|
.Select(t => new { t.Name, t.CreatedDate })
|
|
.ToListAsync();
|
|
foreach (var item in items)
|
|
{
|
|
Console.WriteLine($"{item.Name} - {item.CreatedDate}");
|
|
}
|
|
}
|
|
|
|
async Task SelectQueryDTO()
|
|
{
|
|
var items = await context.Teams
|
|
.Select(t => new TeamInfoDTO { Name = t.Name, Created = t.CreatedDate })
|
|
.ToListAsync();
|
|
foreach (var item in items)
|
|
{
|
|
Console.WriteLine($"{item.Name} - {item.Created}");
|
|
}
|
|
}
|
|
|
|
class TeamInfoDTO
|
|
{
|
|
public DateTime Created { get; set; }
|
|
public string Name { get; set; }
|
|
}
|
|
|
|
class TeamDetailsDTO
|
|
{
|
|
public int TeamId { get; set; }
|
|
public string TeamName { get; set; }
|
|
public string CoachName { get; set; }
|
|
public int TotalHomeGoals { get; set; }
|
|
public int TotalAwayGoals { get; set; }
|
|
} |