Compare commits
14 Commits
23a75282c1
...
10d207cb44
| Author | SHA1 | Date | |
|---|---|---|---|
| 10d207cb44 | |||
| 86b2e60e8c | |||
| f58ce8f120 | |||
| 963378f2e9 | |||
| 5a2a82b413 | |||
| 29aebcea18 | |||
| 915434f44d | |||
| 1aae643a78 | |||
| 51eb8b117a | |||
| b715fd3150 | |||
| f4978d215e | |||
| e542616532 | |||
| 2ef3859d2a | |||
| 61b0e99f94 |
@ -1,2 +1,271 @@
|
|||||||
// See https://aka.ms/new-console-template for more information
|
using EntityFrameworkCore.Data;
|
||||||
Console.WriteLine("Hello, World!");
|
using EntityFrameworkCore.Domain;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
using var context = new DeadBallZoneLeagueDbContext();
|
||||||
|
|
||||||
|
// var teamOne = await context.Teams.FirstAsync(t => t.TeamId == 1);
|
||||||
|
// var teamTwo = await context.Teams.FirstAsync(t => t.TeamId == 2);
|
||||||
|
// var teamThree = await context.Teams.SingleAsync(t => t.TeamId == 3);
|
||||||
|
// var teamFour = await context.Teams.SingleOrDefaultAsync(t => t.TeamId == 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 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.TeamId == 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.TeamId == 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.TeamId));
|
||||||
|
foreach (var team in groupedTeam)
|
||||||
|
{
|
||||||
|
Console.WriteLine(team.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task CountFunctionsTeams(int id)
|
||||||
|
{
|
||||||
|
var numberOfTeams = await context.Teams.CountAsync(t => t.TeamId > id);
|
||||||
|
Console.WriteLine($"Number of teams with ID > {id}: {numberOfTeams}");
|
||||||
|
|
||||||
|
var maxTeams = await context.Teams.MaxAsync(t => t.TeamId);
|
||||||
|
Console.WriteLine($"Max teams: {maxTeams}");
|
||||||
|
|
||||||
|
var minTeams = await context.Teams.MinAsync(t => t.TeamId);
|
||||||
|
Console.WriteLine($"Min teams: {minTeams}");
|
||||||
|
|
||||||
|
var averageTeams = await context.Teams.AverageAsync(t => t.TeamId);
|
||||||
|
Console.WriteLine($"Average teams: {averageTeams}");
|
||||||
|
|
||||||
|
var sumTeams = await context.Teams.SumAsync(t => t.TeamId);
|
||||||
|
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; }
|
||||||
|
}
|
||||||
@ -21,6 +21,7 @@ public class DeadBallZoneLeagueDbContext : DbContext
|
|||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
optionsBuilder.UseSqlite($"Data source={DbPath}")
|
optionsBuilder.UseSqlite($"Data source={DbPath}")
|
||||||
|
// .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking) // Do not use tracking, set globally on all queries.
|
||||||
.LogTo(Console.WriteLine, LogLevel.Information)
|
.LogTo(Console.WriteLine, LogLevel.Information)
|
||||||
.EnableSensitiveDataLogging() // Do not allow this in Production.
|
.EnableSensitiveDataLogging() // Do not allow this in Production.
|
||||||
.EnableDetailedErrors(); // Do not allow this in Production.
|
.EnableDetailedErrors(); // Do not allow this in Production.
|
||||||
|
|||||||
23
README.md
23
README.md
@ -81,3 +81,26 @@ Note that the [Microsoft.EntityFrameworkCore.Tools](https://www.nuget.org/packag
|
|||||||
[Microsoft.EntityFrameworkCore.Sqlite](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/9.0.3)
|
[Microsoft.EntityFrameworkCore.Sqlite](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/9.0.3)
|
||||||
|
|
||||||
* `dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 9.0.3`
|
* `dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 9.0.3`
|
||||||
|
|
||||||
|
## Tips for efficient querying
|
||||||
|
|
||||||
|
* Use indexes.
|
||||||
|
* Use projections.
|
||||||
|
* Limit result set (skip and take)
|
||||||
|
* Use async calls.
|
||||||
|
* Use `FromSQLRaw()` for an optimized query if it gets complex in LINQ.
|
||||||
|
* Use no-tracking.
|
||||||
|
* Use batch operations.
|
||||||
|
|
||||||
|
### Tracking
|
||||||
|
|
||||||
|
Tracking works best when the same DbContext is used for both querying and changing entities. EF Core automatically tracks the state of the queried entities and detects any changes made, until `SaveChanges()` is called, which is a method to save pending changes to the database. These changes are detected with the `DetectChanges()` function. This function can be manually called, to see what the changes are, but it is automatically called by the `SaveChanges()` function. `SaveChanges()` is transactional (for most providers).
|
||||||
|
|
||||||
|
The following states are tracked in the `EntityState.State` property:
|
||||||
|
* **Detached**: entity is not tracked.
|
||||||
|
* **Added**: new entity, not yet added to the database.
|
||||||
|
* **Unchanged**: no changes to the entity.
|
||||||
|
* **Modified**: changes are made to the entity.
|
||||||
|
* **Deleted**: the entity is in the database, but is marked for deletion.
|
||||||
|
|
||||||
|
The `Find` functions require tracking, instead, use the `First` function, with a lambda for the ID.
|
||||||
Loading…
x
Reference in New Issue
Block a user