在系统版本的时间表中查询数据

时间:2019-06-27 08:44:33

标签: c# asp.net entity-framework aspnetboilerplate

我们正在实现一种查询任何表的时态的解决方案。

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

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

从学生中选择*

我们将写: 从学生的FOR SYSTEM_TIME AS OF'2015-09-01 T10:00:00.7230011'中选择*

有什么方法可以在查询结束时自动添加此语句?

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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

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

它支持:

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