Vấn đề:
Tôi đã thu hẹp điều này xuống (những gì có vẻ là) một lỗi trong Pomelo. Vấn đề là ở đây:
https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/issues / 801
Vấn đề là Pomelo tạo defaultValue
thuộc tính cho DateTime
và các cấu trúc khác khi tạo quá trình di chuyển. Nếu một giá trị mặc định được đặt trong quá trình di chuyển, nó sẽ ghi đè chiến lược tạo giá trị và khi đó SQL có vẻ không chính xác.
Cách giải quyết là tạo quá trình di chuyển, sau đó sửa đổi thủ công tệp di chuyển để đặt defaultValue
thành null
(hoặc xóa toàn bộ dòng).
Ví dụ:thay đổi điều này:
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedTime",
table: "SomeTable",
nullable: false,
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)))
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);
Về điều này:
migrationBuilder.AddColumn<DateTime>(
name: "UpdatedTime",
table: "SomeTable",
nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.ComputedColumn);
Sau đó, tập lệnh di chuyển sẽ tạo ra SQL chính xác với DEFAULT CURRENT_TIMESTAMP
cho TIMESTAMP
. Nếu bạn xóa [Column(TypeName = "TIMESTAMP")]
, nó sẽ sử dụng datetime(6)
cột và nhổ ra DEFAULT CURRENT_TIMESTAMP(6)
.
GIẢI PHÁP:
Tôi đã đưa ra một giải pháp thực hiện chính xác Thời gian tạo (chỉ được cập nhật bởi cơ sở dữ liệu trên INSERT) và Thời gian cập nhật (được cập nhật bởi cơ sở dữ liệu chỉ trên INSERT và UPDATE).
Trước tiên, hãy xác định thực thể của bạn như sau:
public class SomeEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Sau đó, thêm phần sau vào OnModelCreating()
:
protected override void OnModelCreating(ModelBuilder builder)
{
// Other model creating stuff here ...
builder.Entity<SomeEntity>.Property(d => d.CreatedTime).ValueGeneratedOnAdd();
builder.Entity<SomeEntity>.Property(d => d.UpdatedTime).ValueGeneratedOnAddOrUpdate();
builder.Entity<SomeEntity>.Property(d => d.CreatedTime).Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
builder.Entity<SomeEntity>.Property(d => d.CreatedTime).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
builder.Entity<SomeEntity>.Property(d => d.UpdatedTime).Metadata.SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
builder.Entity<SomeEntity>.Property(d => d.UpdatedTime).Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
}
Điều này tạo ra một quá trình di chuyển ban đầu hoàn hảo (trong đó migrationBuilder.CreateTable
được sử dụng) và tạo ra SQL mong đợi:
`created_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_time` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
Điều này nên cũng hoạt động trên các di chuyển cập nhật các bảng hiện có, nhưng hãy đảm bảo rằng defaultValue
luôn luôn rỗng.
SetBeforeSaveBehavior
và SetAfterSaveBehavior
dòng ngăn EF cố gắng ghi đè Thời gian đã tạo bằng một giá trị mặc định. Nó làm cho các cột Đã tạo và Cập nhật chỉ được đọc theo quan điểm của EF một cách hiệu quả, cho phép cơ sở dữ liệu thực hiện tất cả công việc.
Bạn thậm chí có thể trích xuất nó thành một giao diện và phương thức mở rộng:
public interface ITimestampedEntity
{
DateTime CreatedTime { get; set; }
DateTime UpdatedTime { get; set; }
}
public static EntityTypeBuilder<TEntity> UseTimestampedProperty<TEntity>(this EntityTypeBuilder<TEntity> entity) where TEntity : class, ITimestampedEntity
{
entity.Property(d => d.CreatedTime).ValueGeneratedOnAdd();
entity.Property(d => d.UpdatedTime).ValueGeneratedOnAddOrUpdate();
entity.Property(d => d.CreatedTime).SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
entity.Property(d => d.CreatedTime).SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
entity.Property(d => d.UpdatedTime).SetBeforeSaveBehavior(PropertySaveBehavior.Ignore);
entity.Property(d => d.UpdatedTime).SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
return entity;
}
Sau đó, triển khai giao diện trên tất cả các thực thể có dấu thời gian của bạn:
public class SomeEntity : ITimestampedEntity
{
// Other properties here ...
public DateTime CreatedTime { get; set; }
public DateTime UpdatedTime { get; set; }
}
Điều này cho phép bạn thiết lập Đối tượng từ bên trong OnModelCreating()
như vậy:
protected override void OnModelCreating(ModelBuilder builder)
{
// Other model creating stuff here ...
builder.Entity<SomeTimestampedEntity>().UseTimestampedProperty();
}