Skip to content

Add migration is always generating code for row version property. #1036

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
XzaR90 opened this issue Mar 3, 2020 · 9 comments
Open

Add migration is always generating code for row version property. #1036

XzaR90 opened this issue Mar 3, 2020 · 9 comments
Assignees
Labels

Comments

@XzaR90
Copy link

XzaR90 commented Mar 3, 2020

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:

@lauxjpn
Copy link
Collaborator

lauxjpn commented Mar 4, 2020

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 lauxjpn self-assigned this Mar 4, 2020
@XzaR90
Copy link
Author

XzaR90 commented Mar 4, 2020

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?

@lauxjpn
Copy link
Collaborator

lauxjpn commented Mar 5, 2020

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

@Macht59
Copy link

Macht59 commented Mar 15, 2020

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

@lauxjpn
Copy link
Collaborator

lauxjpn commented Mar 16, 2020

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

@georgwacker
Copy link

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

@klya-dev
Copy link

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

How did you resolve this issue? I have the same error

@georgwacker
Copy link

georgwacker commented Apr 22, 2025

The project reached EOL, so I stayed at EF6 in the end.

However, looking at the migrations and latest code, I did switch the RowVersion to be not nullable and force a default:

[Timestamp]
public byte[] RowVersion { get; set; } = null!;

@klya-dev
Copy link

Thanks for the answer, but this didn't work on .NET 9.
I came to another answer, works on
Pomelo: 9.0.0-preview.3.efcore.9.0.0
EF: 9.0.4

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

Generated:

migrationBuilder.AddColumn<DateTime>(
    name: "RowVersion",
    table: "Users",
    type: "timestamp(6)",
    rowVersion: true,
    nullable: true);
b.Property<DateTime?>("RowVersion")
    .IsConcurrencyToken()
    .ValueGeneratedOnAddOrUpdate()
    .HasColumnType("timestamp(6)");

This also works, but I don't know how to generate something like this automatically

migrationBuilder.AddColumn<DateTime>(
    name: "RowVersion",
    table: "Users",
    type: "timestamp(6)",
    rowVersion: true,
    nullable: false,
    defaultValue: null); // DateTime is generated, manually set to null

I hope it helped someone

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants