在 Entity Framework Core 中查询系统版本时态表中的数据

Posted

技术标签:

【中文标题】在 Entity Framework Core 中查询系统版本时态表中的数据【英文标题】:Querying Data in a System-Versioned Temporal Table in Entity Framework Core 【发布时间】:2019-11-09 06:02:27 【问题描述】:

我们正在实施一种查询临时表的解决方案。

当在 SQL Server 上为任何表启用临时表时,SQL 将自动在表的末尾添加第二个带有额外“_History”的表以跟踪历史记录。例如,如果我们有一个“student”表,SQL server 将添加“student_History”表。

要查询学生历史,我们只需要查询学生表并在语句末尾添加FOR SYSTEM_TIME AS OF '2015-09-01 T10:00:00.7230011';。 所以不要写:

Select * from student

我们会写:

Select * from student FOR SYSTEM_TIME AS OF '2015-09-01 T10:00:00.7230011'

有没有办法在查询结束时自动追加这条语句?

就像截取查询并应用查询过滤器一样,就像一个软表,但现在它没有过滤,它只是语句末尾的语句。

【问题讨论】:

【参考方案1】:

它可以通过扩展方法来完成,我找到了一段可以帮助你的代码:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Linq;

namespace core

    public static class Extensions
    
        public static void AddTemporalTableSupport(this MigrationBuilder builder, string tableName, string historyTableSchema)
        
            builder.Sql($@"ALTER TABLE tableName ADD 
                            SysStartTime datetime2(0) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL,
                            SysEndTime datetime2(0) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL,
                            PERIOD FOR SYSTEM_TIME (SysStartTime, SysEndTime);");
            builder.Sql($@"ALTER TABLE tableName SET (SYSTEM_VERSIONING = ON (HISTORY_TABLE = historyTableSchema.tableName ));");
        

        public static DbContext GetDbContext<T>(this DbSet<T> dbSet) where T : class
        
            var infrastructure = dbSet as IInfrastructure<IServiceProvider>;
            return (infrastructure.Instance.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext).Context;
        

        public static string GetTableName<T>(this DbSet<T> dbSet) where T : class
        
            var entityType = dbSet.GetDbContext().Model.GetEntityTypes().FirstOrDefault(t => t.ClrType == typeof(T)) 
                ?? throw new ApplicationException($"Entity type typeof(T).Name not found in current database context!");
            var tableNameAnnotation = entityType.GetAnnotation("Relational:TableName");
            return tableNameAnnotation.Value.ToString();
        

        public static IQueryable<T> ForSysTime<T>(this DbSet<T> dbSet, DateTime time) where T : class
        
            return dbSet.FromSql($"SELECT * FROM dbo.[dbSet.GetTableName()] FOR SYSTEM_TIME AS OF 0", time.ToUniversalTime());
        


    

用法

var date = DateTime.Parse("2018-08-28 16:30:00");
var students = ctx.student.ForSysTime(date);

这个扩展方法是Mirek写的,你可以找到完整的文章here。

【讨论】:

嗨,此扩展方法是否适用于 wheres 和 groupsby,以及其他语法适用于 EF Linq? 能否与linq中的其他查询结合使用? 它假定你的架构名称是 dbo!【参考方案2】:

Entity Framework Core (6) 最新版本支持时态表。

正如Microsoft devBlogs 中所述,EF Core 6.0 支持:

使用 EF Core 迁移创建临时表 再次使用迁移将现有表转换为临时表 从过去某个时间点恢复数据 查询历史数据

查询历史数据可以看here

【讨论】:

【参考方案3】:

你可以试试这个 nuget pacakge https://www.nuget.org/packages/EntityFrameworkCore.SqlServer.TemporalTable/

支持:

从 Linq 创建时间查询 临时表迁移。(使用 Add-Migration、Script-Migration)

【讨论】:

以上是关于在 Entity Framework Core 中查询系统版本时态表中的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Entity Framework Core 中运行存储过程?

如何在 Entity Framework Core 中运行存储过程?

什么是自有实体?何时以及为什么在 Entity Framework Core 中使用 Owned Entity?

Entity Framework Core 中 TimeSpan 的总和

Entity Framework优化一:引发了“System.Data.Entity.Core.EntityCommandExecutionException”类型的异常

如何在 Entity Framework Core 2 中播种?