dotnet / EntityFramework.Docs

Documentation for Entity Framework Core and Entity Framework 6
https://docs.microsoft.com/ef/
Creative Commons Attribution 4.0 International
1.62k stars 1.96k forks source link

Document conventions for creating EF6 constraint names #4314

Open maikelvanhaaren opened 1 year ago

maikelvanhaaren commented 1 year ago

Hi! I'm trying to port Entity Framework to EF core. My steps are as follows (if you have suggestions or tips, please let them hear!):

  1. New project in solution for containing EF Core code-first configuration & migrations
  2. Migrate all the Entity Framework configurations to the new EF Core style
  3. Apply EF core migrations to local database next to the existing Entity Framework database
  4. Use SQL Server Schema Comparer to make sure all table-, constraint- and other names are equal to Entity Framework database 4.1. Not equal? Make changes in the EF Core configuration and execute step 3 again. 4.2. Equal? Yeah, next step!
  5. Point EF Core project to Entity Framework database and remove legacy '__MigrationHistory' table
  6. Remove Entity Framework database
  7. Port is finished!

So, I'm currently at step 3/4. I've also noticed that the constraints name of primary and foreign keys are changed in the versions:

EF Core: (..) CONSTRAINT [FK_Entity_ReferringEntity_Id] FOREIGN KEY ([ReferreringEntityId]) REFERENCES [dbo].[ReferringEntity] ([Id]) (..)

Entity Framework: (..) CONSTRAINT [FK_dbo.Entity_dbo.ReferringEntity_Id] FOREIGN KEY ([ReferreringEntityId]) REFERENCES [dbo].[ReferringEntity] ([Id]) (..)

As you can see the scheme ('dbo.') is removed from the constraint name in EF Core. Was this a change within EF Core and is there a way to get it back?

rmmcgr commented 1 year ago

I've faced similar challenges.

I used the following blog post when trying to figure out how I might be able to do this (updating for the changed nature of some of the APIs since this was written): https://www.mikee.se/posts/migrating_from_ef6_to_ef_core

I'm wondering if the highest area of risk in a port is the changed way that EF core manages the schema? This may or may not be a valid concern - I'm sure I don't have enough experience to know - but I have decided to delay my port attempt.

I need to support an existing set of installations for our product, with an existing set of databases, as well as think about new installations where a database will be created from scratch. Does this double my testing load, as each update to the product that includes a migration will need to be tested against a database that started life with EF6, as well as a database that started from EF core? I don't know.

I also had problems with moving over queries that used "GroupBy" with Linq which could not be run under EF core (ATM - I know there are many issues in this repo looking at Group By capabilities).

To address this EF team might consider some type of configuration option that gets EF core to conform to EF6 naming strategies (for PK, FK, indexes) and other schema changes (default values for bools, enums, decimals). Also, a way to turn them off per entity, so over time it would be possible to move to the new naming conventions.

Another idea might be a way to code-gen just the "OnModelCreating" part of a context, from our existing databases that would set all of this up?

Also, a good, comprehensive, porting guide would be great that takes into consideration these complexities, as well as other realities such as continuing to work with a DB that might have started life under EF6 as well as new ones that start under EF core (including any implications with future migrations that might come up).

maikelvanhaaren commented 1 year ago

I've also delayed the port because of the sum of all changes. I noticed that during the try-out port, I'm doing a lot of of the same work (applying EF core migrations to the database, checking schema changes, making changes in EF Core configuration, and repeating...).

I think an EF to EF Core tool would be a really nice addition to the Upgrade-Assistant to make this easier. The ideal flow would be as follows:

  1. Port EF to EF Core with the help of Upgrade-Assistant (making sure naming are equal etc.)
  2. Test locally
  3. Deploy new version to QA/Acceptance/(Prod) with some kind of feature flag to activate EF Core
  4. Test it

@mjrousos confirmed during a standup community live on YouTube that porting EF to EF Core was a part of the Upgrade-Assistant in the beginning, before the GA. It also seems that they are planning to add it (again?) to the tool: https://github.com/dotnet/upgrade-assistant/issues/1451

ajcvickers commented 1 year ago

@maikelvanhaaren You can create a convention that will set the constraint names. For example, this would handle non-composite foreign key constraints:

public class ConstraintsConvention : IModelFinalizingConvention
{
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
    {
        foreach (var entityType in modelBuilder.Metadata.GetEntityTypes())
        {
            foreach (var foreignKey in entityType.GetDeclaredForeignKeys())
            {
                var schema = entityType.GetSchema() ?? "dbo";
                var dependentName = entityType.GetTableName();
                var principalName = foreignKey.PrincipalEntityType.GetTableName();
                var principalKey = foreignKey.PrincipalKey.Properties.Single().Name; // Assumes non-composite PK
                foreignKey.SetConstraintName($"FK_{schema}.{dependentName}_{schema}.{principalName}_{principalKey}");
            }
        }
    }
}

Add this convention in your DbContext:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Add(_ =>  new ConstraintsConvention());
}

Note for triage: we should doc this.