在 Entity Framework 核心中的系统版本时间 Table 中查询数据
Querying Data in a System-Versioned Temporal Table in Entity Framework Core
我们正在实施查询时间 table.
的解决方案
当在 SQL 服务器上为任何 table 启用临时 table 时,SQL 将自动添加第二个 table 和额外的“_History” table 结束跟踪历史。例如,如果我们有一个“学生”table,SQL服务器将添加“student_History”table.
要查询学生历史,我们只需要查询学生table并在语句末尾添加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'
有什么方法可以在查询末尾自动追加这条语句吗?
就像拦截查询并应用查询过滤器一样软table,但现在没有过滤,它只是语句末尾的语句。
可以通过扩展方法来完成,我找到了一段可能对您有帮助的代码:
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);
你可以试试这个 nuget 包 https://www.nuget.org/packages/EntityFrameworkCore.SqlServer.TemporalTable/
支持:
- 从 Linq 创建时间查询
- 临时迁移 table。(使用添加迁移、脚本迁移)
最新版本Entity Framework Core (6) 支持临时表。
如此处Microsoft devBlogs所述,EF Core 6.0 支持:
- 使用 EF Core 迁移创建临时表
- 再次使用迁移将现有表转换为临时表
- 正在恢复过去某个时间点的数据
- 查询历史数据
查询历史数据可以看到here
我们正在实施查询时间 table.
的解决方案当在 SQL 服务器上为任何 table 启用临时 table 时,SQL 将自动添加第二个 table 和额外的“_History” table 结束跟踪历史。例如,如果我们有一个“学生”table,SQL服务器将添加“student_History”table.
要查询学生历史,我们只需要查询学生table并在语句末尾添加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'
有什么方法可以在查询末尾自动追加这条语句吗?
就像拦截查询并应用查询过滤器一样软table,但现在没有过滤,它只是语句末尾的语句。
可以通过扩展方法来完成,我找到了一段可能对您有帮助的代码:
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);
你可以试试这个 nuget 包 https://www.nuget.org/packages/EntityFrameworkCore.SqlServer.TemporalTable/
支持:
- 从 Linq 创建时间查询
- 临时迁移 table。(使用添加迁移、脚本迁移)
最新版本Entity Framework Core (6) 支持临时表。
如此处Microsoft devBlogs所述,EF Core 6.0 支持:
- 使用 EF Core 迁移创建临时表
- 再次使用迁移将现有表转换为临时表
- 正在恢复过去某个时间点的数据
- 查询历史数据
查询历史数据可以看到here