You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Fluent NHibernate查询SQL Server时态表的历史数据?

SQL Server时态表的Fluent NHibernate查询方案

因为NHibernate没有内置支持SQL Server时态表的FOR SYSTEM_TIME AS OF语法,下面提供几种可行的实现方式:

1. 直接使用原生SQL查询

这是最直接的方案,完全贴合你需要的SQL语法,适合临时或简单查询场景:

var targetTime = new DateTime(2022, 12, 28, 21, 0, 0, DateTimeKind.Utc);
var results = session.CreateSQLQuery(@"
    SELECT * FROM [MyDB].[dbo].[MyTable] 
    FOR SYSTEM_TIME AS OF :targetTime
    WHERE YourColumn = :filterValue
")
.SetParameter("targetTime", targetTime, NHibernateUtil.DateTimeOffset)
.SetParameter("filterValue", "yourFilter")
.SetResultTransformer(Transformers.AliasToBean<MyDTO>())
.List<MyDTO>();

注意用参数化查询避免SQL注入,同时通过AliasToBean将查询结果映射到MyDTO。

2. 自定义拦截器实现QueryOver/LINQ支持

如果需要复用时态表查询逻辑,且想保持NHibernate的查询语法风格,可以通过自定义拦截器在SQL生成阶段插入时态表子句:

第一步:实现查询拦截器

public class TemporalTableInterceptor : EmptyInterceptor
{
    private DateTimeOffset? _asOfTime;

    public void SetAsOfTime(DateTimeOffset asOfTime)
    {
        _asOfTime = asOfTime;
    }

    public override SqlString OnPrepareStatement(SqlString sql)
    {
        if (_asOfTime.HasValue && sql.ToString().StartsWith("SELECT", StringComparison.OrdinalIgnoreCase))
        {
            // 提取SQL中的目标表名(简单实现,复杂SQL需调整逻辑)
            var tableMarker = "FROM [MyDB].[dbo].[MyTable]";
            if (sql.ToString().Contains(tableMarker))
            {
                var temporalClause = $" FOR SYSTEM_TIME AS OF '{_asOfTime.Value:yyyy-MM-ddTHH:mm:ss.fffffff}'";
                sql = sql.Replace(tableMarker, tableMarker + temporalClause);
            }
        }
        return base.OnPrepareStatement(sql);
    }
}

第二步:使用拦截器执行QueryOver查询

var asOfTime = new DateTimeOffset(2022, 12, 28, 21, 0, 0, TimeSpan.Zero);
var interceptor = new TemporalTableInterceptor();
interceptor.SetAsOfTime(asOfTime);

using (var session = sessionFactory.WithOptions().Interceptor(interceptor).OpenSession())
{
    var results = session.QueryOver<MyDTO>()
        .Where(x => x.YourColumn == "yourFilter")
        .List();
}

这种方式可以让你继续使用熟悉的QueryOver语法,拦截器会自动为目标表添加时态表子句。

3. 使用NamedQuery封装固定查询逻辑

如果查询逻辑固定,可以将时态表查询定义为命名查询,在Fluent NHibernate映射中配置:

public class MyDTOMap : ClassMap<MyDTO>
{
    public MyDTOMap()
    {
        Table("MyTable");
        // 配置实体属性映射...
        
        NamedQuery("GetMyTableAsOfTime")
            .SetQuery(@"
                SELECT * FROM [MyDB].[dbo].[MyTable] 
                FOR SYSTEM_TIME AS OF :targetTime
                WHERE YourColumn = :filterValue
            ")
            .SetResultTransformer(Transformers.AliasToBean<MyDTO>());
    }
}

调用命名查询:

var targetTime = new DateTimeOffset(2022, 12, 28, 21, 0, 0, TimeSpan.Zero);
var results = session.GetNamedQuery("GetMyTableAsOfTime")
    .SetParameter("targetTime", targetTime)
    .SetParameter("filterValue", "yourFilter")
    .List<MyDTO>();

这种方式适合将常用的时态表查询逻辑封装起来,便于统一维护。

内容的提问来源于stack exchange,提问作者Mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 20:50:31