Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Auto increment non key value entity framework core 2.0

I've got an object that has a key stored as a GUID as well as a friendlyID, like this:

public class Job {

    [Key]
    public Guid Id { get; set; }

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int FriendlyId { get; set; }

    public string Description { get; set; }
}

However when I try to update description using my update method:

    public void Update(Job job, Job jobParam) {
        if (job == null) {
            throw new AppException("Job does not exist");
        }

        //Update job properties
        job.Description = jobParam.Description;


        _context.Job.Update(job);
        _context.SaveChanges();
    }

I get an error stating:

System.Data.SqlClient.SqlException: Cannot update identity column 'FriendlyId'

I've made sure that the correct object is trying to be updated, but I can't understand why friendlyID would try to get updated when it has not been changed. When looking online I can see that there was a bug in EF core 1.1 that would cause this issue to occur, but nothing about 2.0 or about a value that isn't a key.

like image 261
Sean Avatar asked Feb 07 '18 13:02

Sean


1 Answers

The exact behavior of the generated properties in EF Core is still in a process of tweaking. EF Core 2.0 introduced two new property metadata properties - BeforeSaveBehavior and AfterSaveBehavior. There is no data annotation/fluent API for setting them and by default they are implied from value generating strategy and whether the property is part of the key. At the same time they affect the behavior of the tracking operations.

The problem here is that for identity columns (i.e. ValueGeneratedOnAdd) which are not part of a key, the AfterSaveBehavior is Save, which in turn makes Update method to mark them as modified, which in turn generates wrong UPDATE command.

To fix that, you have to set the AfterSaveBehavior like this (inside OnModelCreating override):

modelBuilder.Entity<Job>()
    .Property(e => e.FriendlyId)
    .ValueGeneratedOnAdd()
    .Metadata.AfterSaveBehavior = PropertySaveBehavior.Throw; // <--

Update: In EF Core 3.0+ many metadata properties have been replaced with Get/Set methods, so just use the corresponding method:

.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Throw);
like image 88
Ivan Stoev Avatar answered Nov 14 '22 03:11

Ivan Stoev