This document provides guidance for GitHub Copilot when generating code for the Entity Framework Core project. Follow these guidelines to ensure that generated code aligns with the project's coding standards and architectural principles.
If you are not sure, do not guess, just tell that you don't know or ask clarifying questions. Don't copy code that follows the same pattern in a different context. Don't rely just on names, evaluate the code based on the implementation and usage. Verify that the generated code is correct and compilable.
- Follow the .NET coding guidelines unless explicitly overridden below
- Use the rules defined in the .editorconfig file in the root of the repository for any ambiguous cases
- Write code that is clean, maintainable, and easy to understand
- Favor readability over brevity, but keep methods focused and concise
- Prefer minimal comments - The code should be self-explanatory. Add comments sparingly and only to explain why a non-intuitive solution was necessary, not what the code does. Comments are appropriate for complex logic, public APIs, or domain-specific implementations where context would otherwise be unclear
- Add license header to all files:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
- Don't add the UTF-8 BOM to files unless they have non-ASCII characters
- Avoid breaking public APIs. If you need to break a public API, add a new API instead and mark the old one as obsolete. Use
ObsoleteAttributewith the message pointing to the new API - All types should be public by default
- Types in
.Internalnamespaces or annotated with[EntityFrameworkInternal]require this XML doc comment on ALL members:
/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>- Use spaces for indentation (4 spaces)
- Use braces for all blocks including single-line blocks
- Place braces on new lines
- Limit line length to 140 characters
- Trim trailing whitespace
- All declarations must begin on a new line
- Use a single blank line to separate logical sections of code when appropriate
- Insert a final newline at the end of files
- File scoped namespace declarations
- Use
varfor local variables - Use expression-bodied members where appropriate
- Prefer using collection expressions when possible
- Use
ispattern matching instead ofasfollowed by null checks (e.g.,if (obj is SomeType typed)instead ofvar typed = obj as SomeType; if (typed != null)) - Prefer
switchexpressions overswitchstatements when appropriate - Prefer pattern matching with
whenclauses in switch statements for conditional logic - Prefer field-backed property declarations using field contextual keyword instead of an explicit field.
- Prefer range and index from end operators for indexer access
- The projects use implicit namespaces, so do not add
usingdirectives for namespaces that are already imported by the project - When verifying that a file doesn't produce compiler errors rebuild the whole project
- Use PascalCase for:
- Classes, structs, enums, properties, methods, events, namespaces, delegates
- Public fields
- Static private fields
- Constants
- Use camelCase for:
- Parameters
- Local variables
- Use
_camelCasefor instance private fields - Prefix interfaces with
I - Prefix type parameters with
T - Use meaningful and descriptive names
- Use nullable reference types
- Use proper null-checking patterns
- Use the null-conditional operator (
?.) and null-coalescing operator (??) when appropriate
- Design services to be registered and resolved through the DI container for functionality that could be replaced or extended by users, providers or plug-ins
- Create sealed records with names ending in
Dependenciescontaining the service dependencies
- Follow the existing test patterns in the corresponding test projects
- Create both unit tests and functional tests where appropriate
- Fix
SQLandC#baselines for tests when necessary by setting theEF_TEST_REWRITE_BASELINESenv var to1 - Run tests with project rebuilding enabled (don't use
--no-build) to ensure code changes are picked up - When testing cross-platform code (e.g., file paths, path separators), verify the fix works on both Windows and Linux/macOS
- When testing
dotnet-eftool changes, create a test project and manually run the affected commands to verify behavior
- ALWAYS run
restore.cmd(Windows) or. ./restore.sh(Linux/Mac) first to restore dependencies - ALWAYS run
. .\activate.ps1(PowerShell) or. ./activate.sh(Bash) to set up the development environment with correct SDK versions before building or running the tests - These scripts set
DOTNET_ROOT,DOTNET_MULTILEVEL_LOOKUP, and PATH for the project's specific .NET SDK version
- Include XML documentation for all public APIs
- Add proper
<remarks>tags with links to relevant documentation where helpful - For keywords like
null,trueorfalseuse<see langword="*" />tags - Use
<see href="https://aka.ms/efcore-docs-*">redirects for doc links instead of hardcodedhttps://learn.microsoft.com/links - Include code examples in documentation where appropriate
- Overriding members should inherit the XML documentation from the base type via
/// <inheritdoc />
- Use appropriate exception types.
- ALL user-facing error messages must use string resources from the
.resx(and the generated.Designer.cs) file corresponding to the project - Avoid catching exceptions without rethrowing them
- NEVER hardcode package versions in
.csprojfiles if a centralized version property already exists - Check
eng/Versions.propsfor existing version properties (e.g.,$(SQLitePCLRawVersion)) before adding or updating package references - Use
Directory.Packages.propsfor NuGet package version management with Central Package Management (CPM) - Packages listed in
eng/Version.Details.xmlare managed by Maestro dependency flow and should not be updated manually or by Dependabot
- Provide both synchronous and asynchronous versions of methods where appropriate
- Use the
Asyncsuffix for asynchronous methods - Return
TaskorValueTaskfrom asynchronous methods - Use
CancellationTokenparameters to support cancellation - Avoid async void methods except for event handlers
- Call
ConfigureAwait(false)on awaited calls to avoid deadlocks
- Be mindful of performance implications, especially for database operations
- Avoid unnecessary allocations
- Consider using more efficient code that is expected to be on the hot path, even if it is less readable
- Write code that is secure by default. Avoid exposing potentially private or sensitive data
- Make code NativeAOT compatible when possible. This means avoiding dynamic code generation, reflection, and other features that are not compatible with NativeAOT. If not possible, mark the code with an appropriate annotation or throw an exception
- When implementing state machines or parsers, prefer using enums over multiple boolean flags to track state
- Consolidate related switch statements and avoid duplicating logic across multiple code paths
- When logic requires look-ahead (e.g., detecting multi-character tokens like
/*or--), prefer using indexedforloops overforeachto enable peeking at the next character - After implementing a fix, review the surrounding code for similar patterns that might need the same change
- Use the logging infrastructure for diagnostics and interception
- Prefer using
Check.DebugAssertinstead ofDebug.Assertor comments - Use
Check.NotNullandCheck.NotEmptyfor preconditions in public APIs- The methods in
Checkclass use[CallerArgumentExpression]to automatically capture parameter names
- The methods in
- Unit tests should build the model using the corresponding
*TestHelpers.Instance.CreateConventionBuilder()model and finalizing it - The services in unit tests should be resolved from the
IServiceProviderreturned by*TestHelpers.Instance.CreateContextServices, note that it has overloads allowing to specify the model and mock services - For functional tests, create tests in projects corresponding to the database providers that derive from the appropriate test base classes in the
EFCore.*Specification.Testsprojects - Each provider has its own project structure:
EFCore.{Provider},EFCore.{Provider}.Tests,EFCore.{Provider}.FunctionalTests - Specification tests (
EFCore.*.Specification.Tests) define provider-agnostic functional tests
- src/: Main product source code, including providers, tools, and analyzers
- test/: All test projects, including unit, functional, and specification tests for different providers
- benchmark/: Performance and benchmarking projects for EFCore
- tools/: Utility scripts and resources for development
- eng/: Build and test infrastructure files related to Arcade SDK used for building the project, and running the tests
- docs/: Documentation files for contributors and users. Full documentation is available at EF Core | Learn
- .github/: GitHub-specific files, workflows, and Copilot instructions
- .config/: AzDo pipelines configuration files
- ALWAYS target the
mainbranch for new PRs unless explicitly instructed otherwise - PRs targeting
release/*orfeature/*branches require special permission and are typically only for approved servicing fixes - For servicing PRs (fixes targeting release branches), use the following PR description template:
Fixes #{issue_number}
**Description**
{Brief description of the issue and fix}
**Customer impact**
{How does the reported issue affect customers? Are there workarounds?}
**How found**
{Was it customer reported or found during verification? How many customers are affected?}
**Regression**
{Is it a regression from a released version? Which one?}
**Testing**
{How the changes were tested}
**Risk**
{Low/Medium/High, with justification}
Entity Framework Core (EF Core) is an object-database mapper for .NET. Below is a concise summary of its core architecture and concepts:
DbContextis the main API for interacting with EF Core. It manages entity objects, queries, and changes.- Configuration is done via
OnConfiguringor dependency injection (DI) in modern apps. - Pooling (
AddDbContextPool) reuses context instances for performance in high-throughput scenarios. - Contexts are short-lived; pooling resets state but does not dispose underlying services.
- LINQ queries build expression trees, which EF Core translates into database queries (e.g., SQL).
- The pipeline includes translation, optimization, provider-specific SQL generation, and execution.
- Compiled queries are cached for performance.
- Deferred execution: queries run when enumerated (e.g.,
ToList()).
- Converts raw database results into .NET entity objects.
- Handles property assignment, relationship fix-up, and tracking (if enabled).
- Uses compiled functions for efficient repeated materialization.
- Tracks entity states: Added, Modified, Deleted, Unchanged, Detached.
- Detects changes via snapshots or proxies.
- Drives the update pipeline for
SaveChanges(). - Non-tracking queries (
AsNoTracking) improve read performance.
- Gathers tracked changes and generates database commands (INSERT, UPDATE, DELETE).
- Orders operations to maintain referential integrity.
- Executes in a transaction by default; supports batching.
- Handles concurrency via tokens and exceptions.
ExecuteUpdate/ExecuteDeleteperform set-based updates/deletes directly in the database, bypassing change tracking.- Bulk inserts are batched, but not true multi-row SQL by default.
- The model defines entities, properties, keys, relationships, and mappings.
- Built via conventions, data annotations, and the fluent API (
OnModelCreating). - Pre-convention configuration allows specifying model-wide rules and change used conventions.
- API follows the pattern:
*Builder→IConvention*Builder,IReadOnly*→IMutable*→IConvention*→IRuntime* - The interfaces are implemented by mutable and runtime models that provide the same behavior, but different perf characteristics
- Model is cached for performance.
- Compiled model is generated by
CSharpRuntimeModelCodeGenerator,*CSharpRuntimeAnnotationCodeGeneratorandCSharpDbContextGenerator - Relational providers also use
RelationalModeland*AnnotationProvider
- Entity Types: .NET classes mapped to tables/collections.
- Properties: Scalar, navigation, shadow, or service properties.
- Keys: Primary and alternate keys for identity and relationships.
- Foreign Keys/Relationships: Define associations between entities.
- Navigation Properties: Reference or collection navigations.
- Owned Types: Entity types that are part of an aggregate, no independent identity.
- Complex Types: Value objects embedded in entities, no independent identity.
- Primitive Collections: Collections of scalars, often mapped to JSON columns.
- Maps .NET types to database types, with support for value converters.
- Provider-specific logic handles differences (e.g., SQL Server vs. SQLite).
- Ensures data round-trips correctly between CLR and database.
- Generates model and context code from an existing database schema.
- Useful for starting code-first development from a legacy database.
- EF Core is database-agnostic; providers implement translation, type mapping, and database-specific behaviors.
- Choose the correct provider (e.g., SQL Server, SQLite, PostgreSQL) via NuGet and configuration.
- Incrementally evolve the database schema to match model changes.
- Add, update, or remove migrations; apply them via CLI or code.
- Maintains a migration history table in the database.
dotnet efand Package Manager Console (PMC) provide commands for migrations, scaffolding, and model optimization.- Tools integrate with MSBuild and require the design package.
- A generated static representation of the model for faster startup in large projects.
- Must be regenerated if the model changes.
- Required for NativeAOT scenarios.