4

尝试使用 Entity Framework Core 2.1 插入时态表时出现以下错误。

无法将显式值插入表“db_test.dbo.Department”中的 GENERATED ALWAYS 列。将 INSERT 与列列表一起使用以排除 GENERATED ALWAYS 列,或将 DEFAULT 插入 GENERATED ALWAYS 列。

下面是我的表架构

CREATE TABLE Department   
(    
     DeptID int NOT NULL PRIMARY KEY CLUSTERED, 
     DeptName varchar(50) NOT NULL,
     ManagerID INT  NULL,
     ParentDeptID int NULL,
     SysStartTime datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
     SysEndTime datetime2 GENERATED ALWAYS AS ROW END NOT NULL, 
     PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime)     
)   
WITH    
   (   
      SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.DepartmentHistory)   
   )   
;

我的DbContext样子是这样的:

modelBuilder.Entity<Department>(entity =>
            {
                entity.HasKey(e => e.DeptId);

                entity.Property(e => e.DeptId)
                    .HasColumnName("DeptID")
                    .ValueGeneratedNever();

                entity.Property(e => e.DeptName)
                    .IsRequired()
                    .HasMaxLength(50)
                    .IsUnicode(false);

                entity.Property(e => e.ManagerId).HasColumnName("ManagerID");

                entity.Property(e => e.ParentDeptId).HasColumnName("ParentDeptID");
            });

请看一下,让我知道我们如何解决这个问题。我不喜欢修改 db 上下文类,因为我使用 DBScaffold 通过 nuget 控制台生成它,并且每次重新生成它时我都无法手动更新它。

4

3 回答 3

2

制作期间开始列 (SysStartTime) 和期间结束列 (SysEndTime) 应该可以解决此问题。我们可以通过

ALTER TABLE [dbo].[Department] ALTER COLUMN [SysStartTime] ADD HIDDEN;
ALTER TABLE [dbo].[Department] ALTER COLUMN [SysEndTime] ADD HIDDEN;

我们可以在 sys.columns 表中看到针对这些列的隐藏设置

SELECT * FROM sys.columns WHERE is_hidden = 1 
于 2018-11-08T20:29:56.420 回答
2

TL;博士:

  • entity.Property(e => e.Modified).HasComputedColumnSql("GENERATED ALWAYS AS ROW START");在您的OnModelCreating代码中添加“有效自”列。
  • 您不需要为“有效到”列做任何事情。

背景:

我正在使用 .NET Core 2.2 和 EntityFrameworkCore 3.0 Preview 6。

我没有将HIDDEN属性添加到@VPP 的答案中的那些列,而是发现添加HasComputedColumnSql可以解决问题这也意味着 EF 在每个UPDATE.

这是我的CREATE TABLE

CREATE TABLE [dbo].[Contacts] (
    [ContactId]             INT           NOT NULL IDENTITY, 
    [TenantId]              INT           NOT NULL, 
    [Created]               DATETIME2 (7)                               NOT NULL,
    [Modified]              DATETIME2 (7) GENERATED ALWAYS AS ROW START NOT NULL,
    [ValidTo]               DATETIME2 (7) GENERATED ALWAYS AS ROW END   NOT NULL CONSTRAINT [DF_Contacts_ValidTo] DEFAULT ('9999-12-31 23:59:59.9999999'),
    [Name]                  NVARCHAR(255) NOT NULL, 
    [PhoneNumber]           NVARCHAR(255) NOT NULL, 
    [HasAMulletHaircut]     BIT           NOT NULL, 
    [ListensToDevo]         BIT           NOT NULL, 
    [Status]                INT           NOT NULL, 

    CONSTRAINT PK_Contacts PRIMARY KEY ([ContactId], [TenantId]), 
    CONSTRAINT [FK_Contacts_Tenants] FOREIGN KEY ([TenantId]) REFERENCES dbo.Tenants( TenantId ),

    PERIOD FOR SYSTEM_TIME ([Modified], [ValidTo])
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE=[dbo].[Contacts_History], DATA_CONSISTENCY_CHECK=ON));

这是我的模型生成器代码的样子(最初由 生成dotnet ef dbcontext scaffold,我的修改标记):

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Contact>(entity =>
        {
            entity.HasKey(e => new { e.ContactId, e.TenantId });

            // I added this:
            entity.Property(e => e.Modified).HasComputedColumnSql("GENERATED ALWAYS AS ROW START");
            // I added this, then commented it out because properties cannot have both `HasComputedColumnSql` and `HasDefaultValueSql`. But it works anyway.
            //entity.Property(e => e.ValidTo).HasComputedColumnSql("GENERATED ALWAYS AS ROW END");

            entity.Property(e => e.ValidTo).HasDefaultValueSql("('9999-12-31 23:59:59.9999999')");

            entity.HasOne(d => d.Tenant)
                .WithMany(p => p.Contacts)
                .HasForeignKey(d => d.TenantId)
                .OnDelete(DeleteBehavior.ClientSetNull)
                .HasConstraintName("FK_Contacts_Tenants");
        });

简而言之:

  • 添加entity.Property(e => e.Modified).HasComputedColumnSql("GENERATED ALWAYS AS ROW START");为您的“有效自”列。
  • 您不需要为“有效到”列做任何事情。

我运行了测试并且INSERT语句UPDATE工作正常(请注意,我的代码根本没有修改我的“有效自”或“有效至”属性)。

如上所述,这种方法的优点是在您调用SaveChanges/之后SaveChangesAsync,EF 将自动获取“有效来自”列的更新/新值。

于 2019-07-01T19:56:59.427 回答
0

您可以将有效到/从列的时间日期设置为 Is Hidden = True。然后 EF 不在乎,您的时间版本无需任何额外工作即可工作。当 EF 支持它(3.0ish)时,您应该能够使用 .FromSql($"SELECT * FROM dbo.Products FOR SYSTEM_TIME AS OF {{0}}", date.ToUniversalTime()) 过滤该数据,但大多数前端只关心最近的数据,因此对我们大多数人来说可能不是问题。

于 2019-07-29T16:34:09.003 回答