The property is part of a key and so cannot be modified or marked as modified (ForeignKey is not a key)
Asked Answered
P

1

11

I have two entities with a relationship without a key, when I want to edit the personnel code field in Personnel, this error is displayed to me. 'The property 'Personnel.PersonnelCode' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal.' The EnrolNumberPersonnelNum table also does not have a record that has a relationship with the changed personnel record in my table

personnel class

public class Personnel : BaseEntity, IBaseEntityTypeConfiguration<Personnel>
    {
        public Personnel()
        {
            EnrolNumberPersonnelNums = new HashSet<EnrolNumberPersonnelNum>();
        }
        [Key]
        public int PersonnelId { get; set; }
        public string PersonnelCode { get; set; }
        public virtual ICollection<EnrolNumberPersonnelNum> EnrolNumberPersonnelNums { get; set; }

        public void Map(EntityTypeBuilder<Personnel> builder)
        {
            builder.ToTable("Personnel", "HR");

            builder.Property(e => e.PersonnelCode).HasMaxLength(50);
        }
    }

EnrolNumberPersonnelNum class

 public class EnrolNumberPersonnelNum : BaseEntity, IBaseEntityTypeConfiguration<EnrolNumberPersonnelNum>
    {
        public EnrolNumberPersonnelNum()
        {
        }
        [Key]
        public int EnrolNumberPersonnelNumId { get; set; }
        public string PersonnelCode { get; set; }
        public virtual Personnel Personnel { get; set; }
     
        public void Map(EntityTypeBuilder<EnrolNumberPersonnelNum> builder)
        {
            builder.ToTable("EnrolNumberPersonnelNum", "HR");

            builder.Property(e => e.PersonnelCode)
                .HasMaxLength(50);

            builder.HasOne(c => c.Personnel).WithMany(c => c.EnrolNumberPersonnelNums)
                .HasForeignKey(c => c.PersonnelCode).HasPrincipalKey(c => c.PersonnelCode)
                .OnDelete(DeleteBehavior.ClientNoAction)
                .HasConstraintName($"FK_{nameof(EnrolNumberPersonnelNum)}_{nameof(Personnel)}_{nameof(PersonnelCode)}");
        }
    }

Error message when I want to edit personnel code in personnel entity and EnrolNumberPersonnelNum table is empty .

Polybius answered 13/9, 2022 at 14:11 Comment(0)
E
13

HasPrincipalKey(c => c.PersonnelCode) here

builder.HasOne(c => c.Personnel).WithMany(c => c.EnrolNumberPersonnelNums)
    .HasForeignKey(c => c.PersonnelCode).HasPrincipalKey(c => c.PersonnelCode)
    .OnDelete(DeleteBehavior.ClientNoAction)
    .HasConstraintName($"FK_{nameof(EnrolNumberPersonnelNum)}_{nameof(Personnel)}_{nameof(PersonnelCode)}");

marks the property PersonnelCode of the Personnel entity as alternate key:

An alternate key serves as an alternate unique identifier for each entity instance in addition to the primary key; it can be used as the target of a relationship. When using a relational database this maps to the concept of a unique index/constraint on the alternate key column(s) and one or more foreign key constraints that reference the column(s).

And in EF Core all type of keys (primary and alternate) are read only, i.e. EF Core does not allow updating them (are required and can be provided only for new entities). This is basically what the error message is telling you at the beginning

The property 'Personnel.PersonnelCode' is part of a key and so cannot be modified or marked as modified.

With that being said, there is no solution so far for such type of model, if you need that property editable. The suggestion in the error message

To change the principal of an existing entity with an identifying foreign key, first delete the dependent and invoke 'SaveChanges', and then associate the dependent with the new principal.

doesn't seem viable, since obviously you cannot delete dependents (EnrolNumberPersonnelNums in you case) and then associate them with a new parent (since they are deleted). Eventually you can try loading them in memory, then call RemoveRange, modify the parent key, then SaveChanges, then update PersonnelCode of the cached children instances, call AddRange followed by SaveChanges. Looks unreliable/error prone, but worth trying.

If you are allowed to modify the database, better remove the PersonnelCode from EnrolNumberPersonnelNum and create and use regular FK int PersonnelId bound to the PK of the Personnel. This way you can remove the alternate key (and add just unique constraint if needed) as mentioned in the documentation

Tip

If you just want to enforce uniqueness on a column, define a unique index rather than an alternate key (see Indexes). In EF, alternate keys are read-only and provide additional semantics over unique indexes because they can be used as the target of a foreign key.

That will allow updating the PersonnelCode of Personel as any other property.

Exchange answered 13/9, 2022 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.