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 13 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
99 changes: 65 additions & 34 deletions entity-framework/efcore-and-ef6/porting/index.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,92 @@
---
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: 10/25/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
## When to port

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.
It is not a requirement that you port your code to EF Core. There are many reasons why you may choose to remain on EF6. The latest version of EF6 supports .NET Core, .NET 5 and .NET 6. If your application is running fine and you don't need any of the latest features, you may wish to consider remaining on EF6. Some reasons you might consider porting:

## Behavior changes
- 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.

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.
- 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 EF Core.
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved

### DbSet.Add/Attach and graph behavior
- Modernize your application stack by using dependency injection and seamlessly integrating your data access with technologies like gRPC and GraphQL.

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.
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/).

JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
**EF Core performs a similar recursive search, but with some slightly different rules.**
Regardless of your reasons for considering an upgrade, 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.

* 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.
## Migrations

### Code First database initialization
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.

**EF6 has a significant amount of magic it performs around selecting the database connection and initializing the database. Some of these rules include:**
## Upgrade steps

* If no configuration is performed, EF6 will select a database on SQL Express or LocalDb.
The upgrade path has been split into several documents that are organized by the phase of your upgrade and the type of application.

* If a connection string with the same name as the context is in the applications `App/Web.config` file, this connection will be used.
### Determine your EF Core "flavor"

* If the database does not exist, it is created.
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".

* 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.
**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.

* 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.
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."

**EF Core does not perform any of this magic.**
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.

* The database connection must be explicitly configured in code.
The following table summarizes some high level differences:

* 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).
|**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|

### Code First table naming convention
The hybrid approach is a more advanced approach with additional overhead compared to the traditional code and database approaches.

EF6 runs the entity class name through a pluralization service to calculate the default table name that the entity is mapped to.
### Understand the impact of moving away from EDMX

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.
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.

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.

### Perform the upgrade steps

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:

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.

As for the port itself, at a high level, you will:

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 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)

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).
53 changes: 53 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,53 @@
---
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: 10/27/2016
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.).
JeremyLikness marked this conversation as resolved.
Show resolved Hide resolved
* 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.

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 applications `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.
4 changes: 3 additions & 1 deletion entity-framework/efcore-and-ef6/porting/port-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -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