26

我正在尝试在 NHibernate 4.x 中实现对 SQL Server 2016 时态表的基本支持。这个想法是改变SQL语句

SELECT * FROM Table t0

SELECT * FROM Table FOR SYSTEM_TIME AS OF '2018-01-16 00:00:00' t0

您可以在此处找到有关 SQL Server 2016 中时态表的更多信息

不幸的是,我还没有找到FOR FOR SYSTEM_TIME AS OF '...'在表名和它的别名之间插入语句的任何方法。我不确定自定义方言是否支持这一点。我现在唯一可行的解​​决方案是FOR SYSTEM_TIME在 extra 中附加语句WHERE,我的输出 SQL 看起来像这样

SELECT * FROM Table t0 WHERE FOR SYSTEM_TIME AS OF '2018-01-16 00:00:00'=1

为此,我实现了生成器和方言,如下所示:

public static class AuditableExtensions
{
    public static bool AsOf(this IAuditable entity, DateTime date)
    {
        return true;
    }

    public static IQueryable<T> Query<T>(this ISession session, DateTime asOf) where T : IAuditable
    {
        return session.Query<T>().Where(x => x.AsOf(asOf));
    }
}

public class ForSystemTimeGenerator : BaseHqlGeneratorForMethod
{
    public static readonly string ForSystemTimeAsOfString = "FOR SYSTEM_TIME AS OF";

    public ForSystemTimeGenerator()
    {
        SupportedMethods = new[]
        {
            ReflectionHelper.GetMethod(() => AuditableExtensions.AsOf(null, DateTime.MinValue))
        };
    }

    public override HqlTreeNode BuildHql(MethodInfo method, Expression targetObject, 
        ReadOnlyCollection<Expression> arguments,
        HqlTreeBuilder treeBuilder, 
        IHqlExpressionVisitor visitor)
    {
        return treeBuilder.BooleanMethodCall(nameof(AuditableExtensions.AsOf), new[]
        {
            visitor.Visit(arguments[1]).AsExpression()
        });
    }
}

public class MsSql2016Dialect : MsSql2012Dialect
{
    public MsSql2016Dialect()
    {
        RegisterFunction(nameof(AuditableExtensions.AsOf), new SQLFunctionTemplate(
            NHibernateUtil.Boolean, 
            $"{ForSystemTimeGenerator.ForSystemTimeAsOfString} ?1?2=1"));
    }
}

谁能提供任何更好的方法或示例,我可以用来FOR SYSTEM_TIME AS OF在表名及其别名之间向前移动和插入语句?目前我能看到的唯一解决方案是在其中更改 SQL,OnPrepareStatementSessionInterceptor我相信有一些更好的方法......

4

3 回答 3

2

在NHibernate Reference 的 NHibernate Reference 5.1 中有关于使用 NHibernate 的临时表的信息

19.1 节中的示例显示了如何使用临时表:

首先定义一个过滤器:

<filter-def name="effectiveDate">
<filter-param name="asOfDate" type="date"/>
</filter-def>

然后将其附加到一个类:

<class name="Employee" table="Employee For System_Time All" ...>
   ...
   <many-to-one name="Department" column="dept_id" class="Department"/>
   <property name="EffectiveStartDate" type="date" column="eff_start_dt"/>
   <property name="EffectiveEndDate" type="date" column="eff_end_dt"/>
   ...
   <!--
   Note that this assumes non-terminal records have an eff_end_dt set to
   a max db date for simplicity-sake
   -->
   <filter name="effectiveDate"
   condition=":asOfDate BETWEEN eff_start_dt and eff_end_dt"/>
</class>

然后您需要在会话上启用过滤器:

   ISession session = ...;
   session.EnableFilter("effectiveDate").SetParameter("asOfDate", DateTime.Today);
   var results = session.CreateQuery("from Employee as e where e.Salary > :targetSalary")
   .SetInt64("targetSalary", 1000000L)
   .List<Employee>();

希望这能让人们开始。

于 2019-10-08T08:29:57.813 回答
0

创建一个 UDF(将所需时间作为参数)并调用它,而不是直接使用表。

于 2019-04-28T17:09:27.327 回答
-1
DECLARE @String1 NVARCHAR(100),
    @String2 NVARCHAR(100) ,
    @DateTime DATETIME= '2018-01-16 00:00:00';

 SET @String1 = 'SELECT * FROM Table2';
 SET @String2 = @String1 + ' FOR SYSTEM_TIME AS OF '''
 + CONVERT(VARCHAR(20), @DateTime, 120) + '''';
 SELECT  @String2;
于 2018-05-10T05:47:48.607 回答