NHibernate HQL Generator для поддержки временных таблиц SQL Server 2016
Я пытаюсь реализовать базовую поддержку временных таблиц SQL Server 2016 в NHibernate 4.x. Идея состоит в том, чтобы изменить оператор 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
в дополнительный 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
но я верю, что есть более лучший подход...
Ответы
Ответ 1
Информация об использовании временных таблиц с NHibernate содержится в NHibernate Reference 5.1 по адресу NHibernate Reference
.Пример в разделе 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>();
Надеюсь, это заставит людей начать.
Ответ 2
Создайте UDF (с нужным временем в качестве параметра) и вызывайте его вместо непосредственного использования таблицы.
Ответ 3
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;