Home > database >  How to use a composite primary key with Entity Framework from a foreign key in C#
How to use a composite primary key with Entity Framework from a foreign key in C#

Time:03-18

I’ve got a large problem with an existing database and the primary key / foreign keys using Entity Framework.

It's an existing database and it is not allowed to change the database, at least not the existing keys because they are used by other programs. To explain what is going on with the keys I added a partial diagram.

The parameterCode and Type are string index keys, so the are nog as normal Id's

The database consists of 4 tables:

Parameter

This has the following primary key columns (which form a composite key):

  • ParameterCode
  • Type => this key is part of the parameterType

ParameterType

This has the following primary key:

  • ParameterTypeCode => connected to type in Parameter.

ParameterDocumentType

PrimaryKey: ParameterDocumentId

This has the following foreign keys

  • ParamaterCode => connected to Parameter
  • Type => connected to parameter
  • ParameterDocument => this one works fine, so I keep it out

I created configurations and models to connect all the tables, however the Parameter table connecting to the parameterType does not accept the configuration. When I declare both keys as primary keys.

When connecting the parametertype foreign key which is also part of the primary key I get the following error:

System.InvalidOperationException: 'The property or navigation 'ParameterType' cannot be added to the entity type 'ParameterModel' because a property or navigation with the same name already exists on entity type 'ParameterModel'.'

I stripped the code from all other values to make it smaller.

Datastructure

[![// configurations
public class ParameterConfiguration : IEntityTypeConfiguration<ParameterModel>
{
        public void Configure(EntityTypeBuilder<ParameterModel> builder)
        {
            builder.ToTable("Parameter", table => table.ExcludeFromMigrations());

            builder.HasKey(p => new { p.ParameterCode, p.ParameterType });

            builder.Property(p => p.ParameterCode)
                .HasColumnName("ParameterCode")
                .HasColumnType("varchar(25)")
                .IsRequired();

            builder.Property(p => p.Description)
                .HasColumnName("Description")
                .HasColumnType("varchar(255)");
                .IsRequired();

            builder.HasOne(x => x.ParameterType) // error on creating this key
                .WithMany(x => x.Parameters)
                .HasForeignKey(x => x.Type)
                .IsRequired();
        }
}

// configurations ParameterTypeConfiguration
public class ParameterTypeConfiguration : IEntityTypeConfiguration<ParameterTypeModel>
{
        public void Configure(EntityTypeBuilder<ParameterTypeModel> builder)
        {
            builder.ToTable("ParameterType", table => table.ExcludeFromMigrations());

            builder.HasKey(p => p.ParameterTypeCode);

            builder.Property(p => p.ParameterTypeCode)
                .HasColumnName("ParameterTypeCode")
                .HasColumnType("varchar(50)")
                .IsRequired();

        }
}

// configurations
public void Configure(EntityTypeBuilder<ParameterDocumentTypeModel> builder)
{
            builder.ToTable("ParameterDocumentType", table => table.ExcludeFromMigrations());

            builder.HasKey(p => p.ParameterDocumentTypeId);

            builder.Property(p => p.ParameterDocumentTypeId)
                .HasColumnName("ParameterDocumentTypeID")
                .HasColumnType("bigint")
                .UseIdentityColumn()
                .ValueGeneratedOnAdd()
                .IsRequired();

            builder.Property(p => p.ParameterCode)
                .HasColumnName("Parameter_Code")
                .HasColumnType("varchar(50)")
                .IsRequired();

            builder.Property(p => p.Type)
                .HasColumnName("Type")
                .HasColumnType("varchar(25)")
                .IsRequired();

            builder.HasOne(p => p.Parameter)
                .WithMany(p => p.ParameterDocumentTypes)
                .HasForeignKey(p => p.ParameterCode)
                .IsRequired(false);
        }
}

// models
public class ParameterDocumentTypeModel 
{
        public long ParameterDocumentTypeId { get; set; }
        public long? DocumentTypeId { get; set; }
        
        // used for the connection with parameter
        public virtual ParameterModel Parameter { get; set; }

        public string ParameterTypeCode
        {
            get => Parameter?.Type;
        }
}

public class ParameterModel 
{
        public string ParameterCode { get; set; }
        public string Type { get; set; }

        public virtual ParameterTypeModel ParameterType { get; set; }
        // used to connect to parameterType
        public virtual ICollection<ParameterDocumentTypeModel> ParameterDocumentTypes { get; set; }
}

public class ParameterTypeModel 
{
        public string ParameterTypeCode { get; set; }
        public string Description { get; set; }

        // used to connect to parameter
        public ICollection<ParameterModel> Parameters { get; set; }
}

CodePudding user response:

property or navigation with the same name already exists on entity type 'ParameterModel'

Because you attempted to configure ParameterType as a key property, which would fail later anyway as only scalars can be key properties.

This

  builder.HasKey(p => new { p.ParameterCode, p.ParameterType });

should be

  builder.HasKey(p => new { p.ParameterCode, p.Type });

CodePudding user response:

After correcting: builder.HasKey(p => new { p.ParameterCode, p.ParameterType });

Many thanks for David Browne

I also needed to do the same for the foreignkey.

  • Related