NHibernate HQL Generator для поддержки темпоральных таблиц SQL Server 2016

Я пытаюсь реализовать базовую поддержку временных таблиц SQL Server 2016 в NHibernate 4.x. Идея состоит в том, чтобы изменить оператор SQL с

SELECT * FROM Table t0

to

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 в дополнительный 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 в OnPrepareStatement на SessionInterceptor, но я считаю, что есть лучший подход ...


person veeroo    schedule 16.01.2018    source источник
comment
Последний диалект в репо - MsSql2012   -  person Panagiotis Kanavos    schedule 16.01.2018
comment
Кроме того, предложение FOR SYSTEM_TIME AS OF является подсказкой для таблицы, а не логическим предикатом для предложения Where.   -  person Panagiotis Kanavos    schedule 16.01.2018
comment
Я знаю, можно ли добавить его из генератора?   -  person veeroo    schedule 16.01.2018
comment
Основываясь на комментарии @PanagiotisKanavos, вы можете посмотреть this в качестве отправной точки. Вы не сможете использовать прямое добавление, но вам придется искать имя таблицы и вставлять ... возможно, после псевдонима? Просто идея.   -  person SMM    schedule 18.01.2018
comment
Спасибо, но я ищу решение, в котором мне не придется вручную изменять сгенерированный SQL как SqlString в SessionInterceptor. Все еще верю, что есть лучшее решение с использованием HqlGenerator ... :)   -  person veeroo    schedule 12.02.2018
comment
Вы когда-нибудь придумывали лучшее решение?   -  person Michael    schedule 05.11.2018
comment
Пока нет, извините ... Мы все еще боремся с этим в нашем проекте. Я поделюсь с вами лучшим решением, как только разработаю его :)   -  person veeroo    schedule 07.11.2018
comment
Это хакерство, но можно изменить этот недопустимый SqlString и переместить FOR SYSTEM_TIME AS OF часть перед псевдонимом таблицы, но это возможно только из OnPrepareStatement. Было бы здорово сгенерировать действительный SQL в HqlGenerator, но я не уверен, возможно ли это   -  person veeroo    schedule 09.11.2018


Ответы (2)


Информация об использовании темпоральных таблиц с NHibernate содержится в 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>();

Надеюсь, это заставит людей начать.

person Steve Ford    schedule 08.10.2019
comment
А где в вашем примере темпоральная таблица? Это просто фильтр, добавленный в оператор WHERE - person Roman Artiukhin; 09.10.2019

Создайте UDF (с желаемым временем в качестве параметра) и вызовите его вместо использования таблицы напрямую.

person Razvan Socol    schedule 28.04.2019