Pomelo.EntityFrameworkCore.MySql icon indicating copy to clipboard operation
Pomelo.EntityFrameworkCore.MySql copied to clipboard

Add migration is always generating code for row version property.

Open XzaR90 opened this issue 5 years ago • 9 comments

Steps to reproduce

Add-Migration

    public abstract class EntityBase : IEntityBase
    {
        protected EntityBase();

        [NotMapped]
        public IDictionary<string, string> RuntimeStringProperties { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime? UpdatedAt { get; set; }
        public DateTime? DeletedAt { get; set; }
        public int SortingOrder { get; set; }
        [Timestamp]
        public byte[] RowVersion { get; set; }
    }
    public class SubscriptionCharacterLocationConfiguration : IEntityTypeConfiguration<SubscriptionCharacterLocation>
    {
        public void Configure(EntityTypeBuilder<SubscriptionCharacterLocation> builder)
        {
            builder.HasKey(c => new { c.CharacterId, c.LocationId });

            builder.HasOne(c => c.Character)
               .WithMany(c => c.LocationSubscriptions)
               .HasForeignKey(pc => pc.CharacterId);

            builder.HasOne(c => c.Location)
               .WithMany(p => p.CharacterSubscriptions)
               .HasForeignKey(pc => pc.LocationId);
        }
    }

    [Table("Subscriptions_CharacterLocation")]
    public class SubscriptionCharacterLocation : EntityBase
    {
        public int CharacterId { get; set; }
        public Character Character { get; set; }

        public Guid LocationId { get; set; }
        public Location Location { get; set; }
    }

The issue

RowVersion is always being generated, no issues however with running the solution.

            migrationBuilder.AlterColumn<DateTime>(
                name: "RowVersion",
                table: "Subscriptions_CharacterLocation",
                rowVersion: true,
                nullable: true,
                oldClrType: typeof(DateTime),
                oldType: "timestamp(6)",
                oldNullable: true)
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

Further technical details

MySQL version: new Version(10, 1, 31), ServerType.MariaDb Operating system: Windows 10 Pomelo.EntityFrameworkCore.MySql version: 3.1.1 Microsoft.AspNetCore.App version: 3.1

Other details about my project setup:

XzaR90 avatar Mar 03 '20 09:03 XzaR90

Looks correct to me. You used the [Timestamp] attribute (which results in a rowversion) and you got a rowversion. Depending on your scenario, you could also use the timestamp or timestamp(6) column type in combination with .ValueGeneratedOnAddOrUpdate() to generate something like this:

CREATE TABLE `SubscriptionCharacterLocation` (
  `RowVersion` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

What is it you would expect in your scenario and why do you think the current behavior is/could be a problem?

lauxjpn avatar Mar 04 '20 14:03 lauxjpn

Hi, yes it is correct but it always getting generated on each add migration. If I do other changes it will add the code shown in the issue section. Should it be like that?

XzaR90 avatar Mar 04 '20 21:03 XzaR90

No, this behavior would be unexpected. I will look into that.

lauxjpn avatar Mar 05 '20 12:03 lauxjpn

I have exactly the same problem. Every time I add migration I get following code even if the model was not changed.

            migrationBuilder.AlterColumn<DateTime>(
                name: "Ts",
                table: "WaterMachines",
                rowVersion: true,
                nullable: true,
                oldClrType: typeof(DateTime),
                oldType: "timestamp(6)",
                oldNullable: true)
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

            migrationBuilder.AlterColumn<DateTime>(
                name: "Ts",
                table: "WaterItems",
                rowVersion: true,
                nullable: true,
                oldClrType: typeof(DateTime),
                oldType: "timestamp(6)",
                oldNullable: true)
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

            migrationBuilder.AlterColumn<DateTime>(
                name: "Ts",
                table: "Orders",
                rowVersion: true,
                nullable: true,
                oldClrType: typeof(DateTime),
                oldType: "timestamp(6)",
                oldNullable: true)
                .Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);

Macht59 avatar Mar 15 '20 20:03 Macht59

Making the RowVersion property non-nullable, will workaround this issue:

//
// Using Data Annotations:
//

public class MyEntity
{
    [Required] // <-- Non-Nullable
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

//
// Using FluentAPI:
//

public class MyEntity
{
    public byte[] RowVersion { get; set; }
}

public class MyContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<MyEntity>(entity =>
        {
            entity.Property("RowVersion")
                .IsRequired() // <-- Non-Nullable
                .IsConcurrencyToken()
                .ValueGeneratedOnAddOrUpdate();
        });
    }
}

lauxjpn avatar Mar 16 '20 17:03 lauxjpn

After updating EF Core and Pomelo MySQL to version 7, I would get an error on SaveChanges stating invalid SQL on RETURNING RowVersion, so I tried the mentioned workaround. I'm using MariaDB 10.3.

In my model I have the following, which worked fine with everything on version 6:

[Timestamp]
public byte[]? RowVersion { get; set; }

After changing it to the above workaround like so:

[Required]
[Timestamp]
public byte[] RowVersion { get; set; }

I'm getting the following error trying to apply the migration:

Failed executing DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
ALTER TABLE `R` MODIFY COLUMN `RowVersion` timestamp(6) NOT NULL DEFAULT '0001-01-01 00:00:00' ON UPDATE CURRENT_TIMESTAMP(6);

georgwacker avatar Jun 25 '23 13:06 georgwacker