Pomelo.entityframeworkcore.mysql: Add migration is always generating code for row version property.

Created on 3 Mar 2020  路  5Comments  路  Source: PomeloFoundation/Pomelo.EntityFrameworkCore.MySql

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:

type-bug

Most helpful comment

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

```c#
//
// 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(entity =>
{
entity.Property("RowVersion")
.IsRequired() // <-- Non-Nullable
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate();
});
}
}
```

All 5 comments

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?

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?

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

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);

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

```c#
//
// 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(entity =>
{
entity.Property("RowVersion")
.IsRequired() // <-- Non-Nullable
.IsConcurrencyToken()
.ValueGeneratedOnAddOrUpdate();
});
}
}
```

Was this page helpful?
0 / 5 - 0 ratings