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

Unable to map an enum FK to an integer PK #24568

Open
Tracked by #240
armaniehs opened this issue Apr 2, 2021 · 1 comment
Open
Tracked by #240

Unable to map an enum FK to an integer PK #24568

armaniehs opened this issue Apr 2, 2021 · 1 comment

Comments

@armaniehs
Copy link

G'day EF team!

I've created a project to reproduce this issue in here

https://github.com/armaniehs/enum-issue-ef-5

I've defined my system pre-defined lists as groups of enums

    public enum EListType
    {
        ProjectPhase,
        ProjectStatus
    }

and then for each group I defined the individual list items

    public enum EProjectStatus
    {
        NotApplicable = 0,
        InProgress = 1,
        Closed = 2
    }

    public enum EProjectPhase
    {
        Phase1 = 0,
        Phase2 = 1,
        Phase3 = 2
    }

which are then mapped into ListType and ListItem tables

    public class ListType
    {
        public EListType Id { get; set; }

        public string Description { get; set; }
    }

    public class ListItem
    {
        public EListType ListTypeId { get; set; }

        public virtual ListType ListType { get; set; }

        public int Id { get; set; }

        public string Text { get; set; }
    }

ListItem.Id is defined as integer because the ids are are defined into separate enums, not a big deal so far.

ListItem also has a composite PK

            builder
                .Entity<ListItem>()
                .HasKey(e => new { e.ListTypeId, e.Id });

The issue starts when I try to create FK to ListItem

    public partial class Project
    {
        public long Id { get; set; }

        public string Name { get; set; }

        // Status

        public EListType StatusListTypeId { get; set; }

        public virtual ListType StatusListType { get; set; }

        public EProjectStatus StatusId { get; set; } // FK using enum but PK is an int

        public virtual ListItem Status { get; set; }

        // Phase

        public EListType PhaseListTypeId { get; set; }

        public virtual ListType PhaseListType { get; set; }

        public EProjectPhase PhaseId { get; set; } // FK using enum but PK is an int

        public virtual ListItem Phase { get; set; }
    }

As one can observe, Project.StatusId and Project.PhaseId have been defined using their individual enums in order to restric range. However they are converted to integers in the model.

            builder
                .Entity<Project>()
                .Property(e => e.PhaseId)
                .HasConversion<int>();

            builder
                .Entity<Project>()
                .Property(e => e.StatusId)
                .HasConversion<int>();

When I try to generate the migration it will throw the follow error:

The relationship from 'Project.Phase' to 'ListItem' with foreign key properties {'PhaseListTypeId' : EListType, 'PhaseId' : EProjectPhase} cannot target the primary key {'ListTypeId' : EListType, 'Id' : int} because it is not compatible. Configure a principal key or a set of compatible foreign key properties for this relationship.

It complains that int and EProjectPhase (enum) aren't compatible even though they are.

The two workarounds that I've found so far are:

  1. Define my FKs as integers however I loose the numeric range restriction provided by enums. Not a good option for me.

  2. Don't define the FKs in the model. Instead manually defined the FKs in the Migration

    // Manually add this to Migration Up
    public static class CustomMigrationUp
    {
        public static void HackInForeighKeys(this MigrationBuilder migrationBuilder)
        {
            migrationBuilder.AddForeignKey(
                name: "FK_Project_ListType_PhaseListTypeId_PhaseId",
                table: "Project",
                columns: new[] { "PhaseListTypeId", "PhaseId" },
                principalTable: "ListItem",
                principalColumns: new[] { "ListTypeId", "Id" });

            migrationBuilder.AddForeignKey(
                name: "FK_Project_ListType_StatusListTypeId_StatusId",
                table: "Project",
                columns: new[] { "StatusListTypeId", "StatusId" },
                principalTable: "ListItem",
                principalColumns: new[] { "ListTypeId", "Id" });
        }
    }

The schema generated using ints all the way through and the schema generated by workaround number 2 are identical however option 2 is laborous.

Shouldn't EF allow enums and integers to be used interchangly if .HasConvertion() has been described in the model?

EF Core version: 5.0.3
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 5.0

@ajcvickers
Copy link
Member

@armaniehs EF Core requires that the type of FK properties matches the type of PK properties, other than nullability.

Note from triage: putting this in the backlog to consider relaxing this for enums and other types which could match with simple convert nodes in the change tracking expression trees.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants