Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EF6 to EF Core porting guide #3509

Merged
merged 25 commits into from
Dec 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
02f4a73
EF6 to EF Core porting guide
JeremyLikness Oct 25, 2021
84792c5
Update port-behavior.md
JeremyLikness Oct 25, 2021
9cb7243
Fix name/aliases
JeremyLikness Oct 25, 2021
1c002ec
fix alerts and bullet indentations
JeremyLikness Oct 25, 2021
8069fef
Merge branch 'main' of https://github.com/dotnet/EntityFramework.Docs…
JeremyLikness Oct 26, 2021
e64d2a6
Merge branch 'dotnet:main' into ef6-to-efcore6-port
JeremyLikness Dec 2, 2021
9c975d0
Interim updates
JeremyLikness Dec 3, 2021
384a79e
Merge branch 'main' of https://github.com/dotnet/EntityFramework.Docs…
JeremyLikness Dec 6, 2021
d5b37f9
Continue expanding docs.
JeremyLikness Dec 7, 2021
5220404
Close to first draft!
JeremyLikness Dec 8, 2021
8a916b4
fix end of line
JeremyLikness Dec 8, 2021
5c55538
Final cleanup for first draft
JeremyLikness Dec 8, 2021
da8b2cd
fix description
JeremyLikness Dec 8, 2021
ddbd061
Update entity-framework/efcore-and-ef6/porting/port-database.md
JeremyLikness Dec 9, 2021
a217002
Fix dates
JeremyLikness Dec 9, 2021
5ecb7aa
Merge branch 'ef6-to-efcore6-port' of https://github.com/JeremyLiknes…
JeremyLikness Dec 9, 2021
3413337
Address items from review
JeremyLikness Dec 9, 2021
26487d0
fix lint error
JeremyLikness Dec 9, 2021
56aa80e
Update index.md
JeremyLikness Dec 10, 2021
323a0ac
Update index.md
JeremyLikness Dec 10, 2021
1f95629
Update index.md
JeremyLikness Dec 10, 2021
eafe7d9
Update port-database.md
JeremyLikness Dec 13, 2021
a37f3a8
Merge branch 'dotnet:main' into ef6-to-efcore6-port
JeremyLikness Dec 14, 2021
2be49ad
address arthur's comments
JeremyLikness Dec 14, 2021
772956c
final tweak
JeremyLikness Dec 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 62 additions & 34 deletions entity-framework/efcore-and-ef6/porting/index.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,89 @@
---
title: Porting from EF6 to EF Core - EF
description: General information on porting an application from Entity Framework 6 to Entity Framework Core
author: ajcvickers
ms.date: 10/27/2016
title: Port from EF6 to EF Core - EF
description: A detailed guide to port your EF6 apps to EF Core
author: jeremylikness
ms.alias: jeliknes
ms.date: 12/09/2021
uid: efcore-and-ef6/porting/index
---
# Porting from EF6 to EF Core
# Port from EF6 to EF Core

Because of the fundamental changes in EF Core we do not recommend attempting to move an EF6 application to EF Core unless you have a compelling reason to make the change.
You should view the move from EF6 to EF Core as a port rather than an upgrade.
Entity Framework Core, or EF Core for short, is a total rewrite of Entity Framework for modern application architectures. Due to fundamental changes, there is not a direct upgrade path. The purpose of this documentation is to provide an end-to-end guide for porting your EF6 applications to EF Core.

> [!IMPORTANT]
> Before you start the porting process it is important to validate that EF Core meets the data access requirements for your application.
> Before you start the porting process it is important to validate that EF Core meets the data access requirements for your application. You can find everything you need in the [EF Core documentation](/ef/core/).

## Missing features
> [!IMPORTANT]
> There is a known issue ([microsoft/dotnet-apiport #993](https://github.com/microsoft/dotnet-apiport/issues/993)) with the [portability analyzer](/dotnet/standard/analyzers/portability-analyzer) that erroneously reports EF Core as incompatible with .NET 5 and .NET 6. These warnings can be safely ignored as EF Core is 100% compatible with .NET 5 and .NET 6 target frameworks.

## Reasons to upgrade

All new Entity Framework development is happening in EF Core. There are no plans to backport any new features to EF6. EF Core runs on the latest .NET runtimes and takes full advantage of runtime, platform-specific (such as ASP.NET Core or WPF) and language-specific features. Here are a few of the benefits you gain from upgrading:

JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
- Take advantage of the ongoing **performance improvements** in EF Core. For example, one customer who migrated from EF6 to EF Core 6 saw a 40x reduction in use of a heavy query due to the [query splitting feature](/ef/core/querying/single-split-queries/). Many customers report enormous performance gains simply by moving to the latest EF Core.
- Use **new features** in EF Core. There will be no new features added to EF6. All of the new functionality, for example the [Azure Cosmos DB provider](/ef/core/providers/cosmos/) and [`DbContextFactory`](/ef/core/what-is-new/ef-core-5.0/whatsnew#dbcontextfactory), will only be added to EF Core. For a full comparison of EF6 to EF Core, including several features exclusive to EF Core, see: [Compare EF Core & EF6](/ef/efcore-and-ef6/).
- **Modernize your application stack** by using dependency injection and seamlessly integrate your data access with technologies like gRPC and GraphQL.

## A note on migrations

Make sure that EF Core has all the features you need to use in your application. See [Feature Comparison](xref:efcore-and-ef6/index) for a detailed comparison of how the feature set in EF Core compares to EF6. If any required features are missing, ensure that you can compensate for the lack of these features before porting to EF Core.
This documentation uses the terms _port_ and _upgrade_ to avoid confusion with the term [_migrations_](/ef/core/managing-schemas/migrations/) as a feature of EF Core. Migrations in EF Core are not compatible with [EF6 Code First migrations](/ef/ef6/modeling/code-first/migrations/) due to significant improvements to how migrations are handled. There is not a recommended approach to port your migrations history, so plan to start "fresh" in EF Core. You can maintain the codebase and data from your EF6 migrations. Apply your final migration in EF6, then create an initial migration in EF Core. You will be able to track history in EF Core moving forward.

## Behavior changes
## Upgrade steps

This is a non-exhaustive list of some changes in behavior between EF6 and EF Core. It is important to keep these in mind as your port your application as they may change the way your application behaves, but will not show up as compilation errors after swapping to EF Core.
The upgrade path has been split into several documents that are organized by the phase of your upgrade and the type of application.

### DbSet.Add/Attach and graph behavior
### Determine your EF Core "flavor"

In EF6, calling `DbSet.Add()` on an entity results in a recursive search for all entities referenced in its navigation properties. Any entities that are found, and are not already tracked by the context, are also marked as added. `DbSet.Attach()` behaves the same, except all entities are marked as unchanged.
There are several approaches to how EF Core works with your domain model and database implementation. In general, most apps will follow one of these patterns and how you approach your port will depend on the application "flavor".

**EF Core performs a similar recursive search, but with some slightly different rules.**
**Code as the source of truth** is an approach in which everything is modeled through code and classes, whether through data attributes, fluent configuration, or a combination of both. The database is initially generated based on the model defined in EF Core and further updates are typically handled through migrations. This is often referred to as "code first," but the name isn't entirely accurate because one approach is to start with an existing database, generate your entities, and then maintain with code moving forward.

* The root entity is always in the requested state (added for `DbSet.Add` and unchanged for `DbSet.Attach`).
* **For entities that are found during the recursive search of navigation properties:**
* **If the primary key of the entity is store generated**
* If the primary key is not set to a value, the state is set to added. The primary key value is considered "not set" if it is assigned the CLR default value for the property type (for example, `0` for `int`, `null` for `string`, etc.).
* If the primary key is set to a value, the state is set to unchanged.
* If the primary key is not database generated, the entity is put in the same state as the root.
The **Database as source of truth** approach involves reverse-engineering or scaffolding your code from the database. When schema changes are made, the code is either regenerated or updated to reflect the changes. This is often called "database first."

### Code First database initialization
Finally, a more advanced **Hybrid mapping** approach follows the philosophy that the code and database are managed separately, and EF Core is used to map between the two. This approach typically eschews migrations.

**EF6 has a significant amount of magic it performs around selecting the database connection and initializing the database. Some of these rules include:**
The following table summarizes some high level differences:

* If no configuration is performed, EF6 will select a database on SQL Express or LocalDb.
|**Approach**|**Developer role**|**DBA role**|**Migrations**|**Scaffolding**|**Repo**|
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
|---|---|---|---|---|---|
|**Code first**|Design entities and verify/customize generated migrations|Verify schema definitions and changes|Per commit|N/A|Track entities, DbContext, and migrations|
|**Database first**|Reverse engineer after changes and verify generated entities|Inform developers when the database changes to re-scaffold|N/A|Per schema change|Track extensions/partial classes that extend the generated entities|
|**Hybrid**|Update fluent configuration to map whenever entities or database change|Inform developers when the database has changed so they can update entities and model configuration|N/A|N/A|Track entities and DbContext|

* If a connection string with the same name as the context is in the applications `App/Web.config` file, this connection will be used.
The hybrid approach is a more advanced approach with additional overhead compared to the traditional code and database approaches.

* If the database does not exist, it is created.
### Understand the impact of moving away from EDMX

* If none of the tables from the model exist in the database, the schema for the current model is added to the database. If migrations are enabled, then they are used to create the database.
EF6 supported a special model definition format named *Entity Data Model XML (EDMX)*. EDMX files contain multiple definitions, including conceptual schema definitions (CSDL), mapping specifications (MSL), and store schema definitions (SSDL). EF Core tracks the domain, mapping, and database schemas through internal model graphs and does not support the EDMX format. Many blog posts and articles mistakenly state this means EF Core only supports "code first." EF Core supports all three application models described in the previous section. You can rebuild the model in EF Core by [reverse-engineering the database](/ef/core/managing-schemas/scaffolding). If you use EDMX for a visual representation of your entity model, consider using the open source [EF Core Power Tools](https://github.com/ErikEJ/EFCorePowerTools) that provide similar capabilities for EF Core.

* If the database exists and EF6 had previously created the schema, then the schema is checked for compatibility with the current model. An exception is thrown if the model has changed since the schema was created.
For more information on the impact of lack of support for EDMX files, read the [porting EDMX](/efcore-and-ef6/porting/port-edmx#other-considerations) guide.

**EF Core does not perform any of this magic.**
### Perform the upgrade steps

* The database connection must be explicitly configured in code.
It is not a requirement to port the entire application. EF6 and EF Core can run in the same application (see: [using EF Core and EF6 in the same application](/ef/efcore-and-ef6/side-by-side?branch=pr-en-us-3509)). To minimize risk, you might consider:

* No initialization is performed. You must use `DbContext.Database.Migrate()` to apply migrations (or `DbContext.Database.EnsureCreated()` and `EnsureDeleted()` to create/delete the database without using migrations).
1. Move to EF6 on .NET Core if you haven't already.
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
1. Migrate a small portion of your app to EF Core and run it side-by-side with EF6.
1. Eventually bring the rest of the codebase to EF Core and retire the EF6 code.

### Code First table naming convention
As for the port itself, at a high level, you will:

EF6 runs the entity class name through a pluralization service to calculate the default table name that the entity is mapped to.
1. [Review behavior changes between EF6 and EF Core](/efcore-and-ef6/porting/port-behavior).
1. Perform your final migrations, if any, in EF6.
1. Create your EF Core project.
1. Either copy code to the new project, run reverse-engineering, or a combination of both.
1. Rename references and entities and update behaviors:
- `System.Data.Entity` to `Microsoft.EntityFrameworkCore`
- Change `DbContext` constructor to consume options and/or override `OnConfiguring`
- `DbModelBuilder` to `ModelBuilder`
- Rename `DbEntityEntry<T>` to `EntityEntry<T>`
- Move from `Database.Log` to `Microsoft.Extensions.Logging` (advanced) or `DbContextOptionsBuilder.LogTo` (simple) APIs
- Apply changes for `WithRequired` and `WithOptional` (see [here](/efcore-and-ef6/porting/port-detailed-cases#required-and-optional))
- Update validation code. There is no data validation built into EF Core, but you can [do it yourself](/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/domain-model-layer-validations#use-validation-attributes-in-the-model-based-on-data-annotations).
- Follow any necessary steps to [port from EDMX](/efcore-and-ef6/porting/port-edmx).
1. Perform specific steps based on your EF Core approach:
- [Code as source of truth](/efcore-and-ef6/porting/port-code.md)
- [Database as source of truth](/efcore-and-ef6/porting/port-database.md)
- [Hybrid model](/efcore-and-ef6/porting/port-hybrid.md)

EF Core uses the name of the `DbSet` property that the entity is exposed in on the derived context. If the entity does not have a `DbSet` property, then the class name is used.
There are many considerations that relate to all of the approaches, so you will also want to review ways to address and work around the [detailed differences between EF6 and EF Core](/efcore-and-ef6/porting/port-detailed-cases.md).
56 changes: 56 additions & 0 deletions entity-framework/efcore-and-ef6/porting/port-behavior.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
title: Porting from EF6 to EF Core - Behavior changes
description: A non-exhaustive list of some changes in behavior between EF6 and EF Core.
author: ajcvickers
ms.date: 12/09/2021
uid: efcore-and-ef6/porting/port-behavior
---
# Behavior Changes between EF6 and EF Core

This is a non-exhaustive list of changes in behavior between EF6 and EF Core. It is important to keep these in mind as your port your application as they may change the way your application behaves, but will not show up as compilation errors after swapping to EF Core.

This is meant as a high level review to consider as part of the porting process. For more detailed, case-by-case instructions, read the [detailed cases](/efcore-and-ef6/porting/port-detailed-cases).

## DbSet.Add/Attach and graph behavior

In EF6, calling `DbSet.Add()` on an entity results in a recursive search for all entities referenced in its navigation properties. Any entities that are found, and are not already tracked by the context, are also marked as added. `DbSet.Attach()` behaves the same, except all entities are marked as unchanged.

JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
**EF Core performs a similar recursive search, but with some slightly different rules.**

- If the root entity is configured for a generated key and the key is not set, it will be put into the `Added` state.
- **For entities that are found during the recursive search of navigation properties:**
- **If the primary key of the entity is store generated**
- If the primary key is not set to a value, the state is set to added. The primary key value is considered "not set" if it is assigned the CLR default value for the property type (for example, `0` for `int`, `null` for `string`, etc.).
- If the primary key is set to a value, the state is set to unchanged.
- If the primary key is not database generated, the entity is put in the same state as the root.
- This change of behavior only applies to the `Attach` and `Update` method groups. `Add` always puts entities in the `Added` state, even if the key is set.
- `Attach` methods put entities with keys set into the `Unchanged` state. This facilitates "insert it if new, otherwise leave it alone." `Update` methods put entities with keys set into the `Modified` state. This facilitates "insert it if new, otherwise update it."

The general philosophy here is that `Update` is a very simple way to handle inserts and updates of disconnected entities. It ensures any new entities are inserted, and any existing entities are updated.

At the same time, `Add` still provides an easy way force entities to be inserted. Add is mostly useful only when not using store-generated keys, such that EF doesn't know if the entity is new or not.

For more information on these behaviors in EF Core, read [Change Tracking in EF Core](/ef/core/change-tracking/).

## Code First database initialization

**EF6 has a significant amount of magic it performs around selecting the database connection and initializing the database. Some of these rules include:**
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved

- If no configuration is performed, EF6 will select a database on SQL Express or LocalDb.
- If a connection string with the same name as the context is in the application's `App/Web.config` file, this connection will be used.
- If the database does not exist, it is created.
- If none of the tables from the model exist in the database, the schema for the current model is added to the database. If migrations are enabled, then they are used to create the database.
- If the database exists and EF6 had previously created the schema, then the schema is checked for compatibility with the current model. An exception is thrown if the model has changed since the schema was created.

**EF Core does not perform any of this magic.**

- The database connection must be explicitly configured in code.
- No initialization is performed. You must use `DbContext.Database.Migrate()` to apply migrations (or `DbContext.Database.EnsureCreated()` and `EnsureDeleted()` to create/delete the database without using migrations).

## Code First table naming convention

EF6 runs the entity class name through a pluralization service to calculate the default table name that the entity is mapped to.

EF Core uses the name of the `DbSet` property that the entity is exposed in on the derived context. If the entity does not have a `DbSet` property, then the class name is used.

For more information, read [Managing Database Schemas](/ef/core/managing-schemas/).
6 changes: 4 additions & 2 deletions entity-framework/efcore-and-ef6/porting/port-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Porting from EF6 to EF Core - Porting a Code-Based Model - EF
description: Specific information on porting an Entity Framework 6 code-based model application to Entity Framework Core
author: ajcvickers
ms.date: 10/27/2016
ms.date: 12/09/2021
uid: efcore-and-ef6/porting/port-code
---
# Porting an EF6 Code-Based Model to EF Core
Expand All @@ -23,7 +23,7 @@ Most APIs that you use in EF6 are in the `System.Data.Entity` namespace (and rel

## Context configuration (connection etc.)

As described in [Ensure EF Core Will Work for Your Application](xref:efcore-and-ef6/porting/index), EF Core has less magic around detecting the database to connect to. You will need to override the `OnConfiguring` method on your derived context, and use the database provider specific API to setup the connection to the database.
As described in [configuring the database connection](/ef/efcore-and-ef6/porting/port-detailed-cases?#configuring-the-database-connection), EF Core has less magic around detecting the database to connect to. You will need to override the `OnConfiguring` method on your derived context, and use the database provider specific API to setup the connection to the database.
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved

Most EF6 applications store the connection string in the applications `App/Web.config` file. In EF Core, you read this connection string using the `ConfigurationManager` API. You may need to add a reference to the `System.Configuration` framework assembly to be able to use this API.

Expand Down Expand Up @@ -53,3 +53,5 @@ If possible, it is best to assume that all previous migrations from EF6 have bee
## Test the port

Just because your application compiles, does not mean it is successfully ported to EF Core. You will need to test all areas of your application to ensure that none of the behavior changes have adversely impacted your application.

Finally, review the [detailed cases to consider when porting](/ef/efcore-and-ef6/porting/port-detailed-cases) for more advice on specific cases and scenarios in your code.
Loading