2025-04-14 22:33:11 +02:00

225 lines
11 KiB
Markdown

# Entity Framework - Dead Ball Zone
This is a practice project to learn Entity Framework.
Using the 1998 game [Dead Ball Zone](https://en.wikipedia.org/wiki/Dead_Ball_Zone) for inspiration.
## .NET commands:
### NuGet commands
Install [Entity Framework Core NuGet package](https://www.nuget.org/packages/microsoft.entityframeworkcore):
`dotnet add package Microsoft.EntityFrameworkCore --version 9.0.3`
### Creation commands
To list all available templates:
`dotnet new -l`
New class:
`dotnet new class -n MyClass`
adding to an existing solution:
`dotnet sln add Data/Data.csproj`
### Entity Framework commands
Create a migration, inside of the Console project, referring to the Data project:
`dotnet ef migrations add InitialMigration --startup-project ./ --project ../EntityFrameworkCore.Data`
Show migration status (`Get-Migration` has more details):
`dotnet ef migrations list --startup-project ./ --project ../EntityFrameworkCore.Data`
Update the database:
`dotnet ef database update --startup-project ./ --project ../EntityFrameworkCore.Data`
Database-first scaffolding example: *(no need for escape slashes)*
`Scaffold-DbContext 'connection-string' Microsoft.EntityFrameworkCore.SqlServer -ContextDir ScaffoldDbContext -OutputDir ScallfoldModels`
`dotnet ef dbcontext scaffold "connection-string" Microsoft.EntityFrameworkCore.SqlServer --context-dir ScaffoldDbContext --output-dir ScaffoldModels --startup-project ./ --project ..\EntityFrameworkCore.Data\`
Using `-force` parameter to force overrides when scaffolding.
Remove last migration:
`dotnet ef migrations remove --startup-project ./ --project ../EntityFrameworkCore.Data`
Create migration script (This generates SQL of all the migrations). Add the `--idempotent` parameter to make it idempotent. This applies only the changes not already made. You can also specify migration names to only generate between specific migrations.
`dotnet ef migrations script --startup-project ./ --project ../EntityFrameworkCore.Data`
Rollback to specific migration, use `list` command to see (Pending) changes:
`dotnet ef database update 20250406180432_AddMatchAndLeagueEntities --startup-project ./ --project ../EntityFrameworkCore.Data`
To re-apply changes again, simply do a `database update` again. Beware that data will be lost.
When this error appears:
`Unable to create a 'DbContext' of type 'RuntimeType'. The exception 'The configuration file 'appsettings.json' was not found and is not optional.`
Then you should either provide an `appsettings.json` file in the project, or simply change the startup project.
## Links
* [Entity Framework - Database providers](https://learn.microsoft.com/en-us/ef/core/providers/?tabs=dotnet-core-cli)
## Terms
A **database-context** is an abstraction of the database structure in code.
* It lists the models and their database table names.
* It instantiates a database connection during the runtime of the application.
* Allows configurations to be made in code.
The **migrations** provide a version-controlled method to maintain the state of the database.
* It is possible to track when, and by who changes were made.
* In the Up method, it is described which changes should be made.
* in the Down method, it is described which changes should be undone. This allows rollback.
* There is a history table that is kept by default to track migrations.
## Installed NuGet packages
A list of installed NuGet packages in this application.
Note that the [Microsoft.EntityFrameworkCore.Tools](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools/9.0.3) is used for Powershell commands used in the Package Manager Console for Visual Studio and that [Microsoft.EntityFrameworkCore.Design](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design/9.0.3) is used for cross-platform command tools.
### API project
[Microsoft.VisualStudio.Web.CodeGeneration.Design](https://www.nuget.org/packages/Microsoft.VisualStudio.Web.CodeGeneration.Design/9.0.0)
* `dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design --version 9.0.0`
[Microsoft.EntityFrameworkCore.Design](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Design/9.0.3)
* `dotnet add package Microsoft.EntityFrameworkCore.Design --version 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`
[Microsoft.EntityFrameworkCore.Tools](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools/9.0.3) *Required for aspnet-codegenerator cli tool.*
* `dotnet add package Microsoft.EntityFrameworkCore.Tools --version 9.0.3`
### Console project
[Microsoft.EntityFrameworkCore.Tools](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Tools/9.0.3)
* `dotnet add package Microsoft.EntityFrameworkCore.Tools --version 9.0.3`
### Data project
[Microsoft.EntityFrameworkCore](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore/9.0.3)
* `dotnet add package Microsoft.EntityFrameworkCore --version 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`
[Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration/9.0.3)
* `dotnet add package Microsoft.Extensions.Configuration --version 9.0.3`
[Microsoft.Extensions.Configuration.FileExtensions](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.FileExtensions/9.0.3)
* `dotnet add package Microsoft.Extensions.Configuration.FileExtensions --version 9.0.3`
[Microsoft.Extensions.Configuration.Json](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Json/9.0.3)
* `dotnet add package Microsoft.Extensions.Configuration.Json --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 types
Entity Framework Core has 3 common loading patterns.
* **Eager loading**: related data is loaded from the database as part of the initial query.
* **Explicit loading**: related data is explicitly loaded from the database later.
* **Lazy loading**: related data is loaded from the database accessing navigation property.
#### Eager loading
Uses the `include()` function to load related data in a query. The eager loading of a collection navigation may cause a performance issue, use `AsSplitQuery()` as needed to boost performance with large data sets. The `include()` function allows the `Where()` function to be used for filtering. If *tracking* is enabled, then the filter may return *incorrect* data, because it returns tracked records. Data may already be updated in the database, while the application is still using tracked data. Use the `ThenInclude()` function to get more dependencies.
### Explicit loading
Data can be explicitly loaded by executing a separate query that returns related entities. If *tracking* is enabled, the navigation properties of the newly loaded entity will refer to any entities already loaded.
### Lazy loading
This is the least recommended method of loading data. It requires the [Microsoft.EntityFrameworkCore.Proxies](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Proxies/9.0.3) package. It must then be enabled in the DbContext configuration with `UseLazyLoadingProxies()`. Navigation properties must be virtual. It can cause extra database roundtrips, causing the [N+1 problem](https://docs.sentry.io/product/issues/issue-details/performance-issues/n-one-queries/).
## Database
### Relationships
* A **League** has one or more **Teams** (1:M)
* A **Team** has one **Coach** (1:1)
* **Matches** are played by many **Teams** (M:M)
You can use [EF Core Power Tools](https://marketplace.visualstudio.com/items?itemName=ErikEJ.EFCorePowerTools) to create diagrams.
## ASP.NET Core with Entity Framework Core
A web application will make connection to the database on demand, which is a scoped connection. When not using the database, the web application is in a disconnected state.
You cannot create a new context in a file, because a new connection needs to be established every time and also closed, when the database is no longer needed. Also, *no tracking* is recommended, because multiple people can manipulate data.
An Inversion of Control (IoC) container will be used to handle dependancy injection for the DbContext. And also the DbContext will need a constructor with parameters to initialize itself properly.
## Scaffolding
### Scaffolding tool
[ASP.NET Core code generator tool](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/tools/dotnet-aspnet-codegenerator?view=aspnetcore-9.0).
* `dotnet tool install -g dotnet-aspnet-codegenerator` or, if already installed: `update`
To run this tool:
* `dotnet aspnet-codegenerator controller -name TeamsController -async -api -m Team -dc DeadBallZoneLeagueDbContext -outDir Controllers -dbProvider sqlite`
Note that an issue may arise, related to this: https://github.com/dotnet/Scaffolding/issues/3145
## API
Since .NET 9, the OpenAPI standard has been defined. To view documentation of our API, Swagger UI can be installed separately.
See also: https://devblogs.microsoft.com/dotnet/dotnet9-openapi/
* [https://swagger.io/tools/swagger-ui/](https://swagger.io/tools/swagger-ui/)
* [Swagger UI installation](https://github.com/swagger-api/swagger-ui/blob/HEAD/docs/usage/installation.md)
Or use:
* [install-swagger-tooling](https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-web-api?view=aspnetcore-9.0&tabs=visual-studio-code#install-swagger-tooling)