Вы можете создать класс, который реализует как IPreUpdateEventListener
, так и IPreInsertEventListener
следующим образом:
public class InsertUpdateListener : IPreInsertEventListener, IPreUpdateEventListener {
public bool OnPreInsert(PreInsertEvent @event) {
CheckDateTimeWithinSqlRange(@event.Persister, @event.State);
return false;
}
public bool OnPreUpdate(PreUpdateEvent @event) {
CheckDateTimeWithinSqlRange(@event.Persister, @event.State);
return false;
}
private static void CheckDateTimeWithinSqlRange(IEntityPersister persister, IReadOnlyList<object> state) {
var rgnMin = System.Data.SqlTypes.SqlDateTime.MinValue.Value;
// There is a small but relevant difference between DateTime.MaxValue and SqlDateTime.MaxValue.
// DateTime.MaxValue is bigger than SqlDateTime.MaxValue but still within the valid range of
// values for SQL Server. Therefore we test against DateTime.MaxValue and not against
// SqlDateTime.MaxValue. [Manfred, 04jul2017]
//var rgnMax = System.Data.SqlTypes.SqlDateTime.MaxValue.Value;
var rgnMax = DateTime.MaxValue;
for (var i = 0; i < state.Count; i++) {
if (state[i] != null
&& state[i] is DateTime) {
var value = (DateTime)state[i];
if (value < rgnMin /*|| value > rgnMax*/) { // we don't check max as SQL Server is happy with DateTime.MaxValue [Manfred, 04jul2017]
throw new ArgumentOutOfRangeException(persister.PropertyNames[i], value,
$"Property '{persister.PropertyNames[i]}' for class '{persister.EntityName}' must be between {rgnMin:s} and {rgnMax:s} but was {value:s}");
}
}
}
}
}
Вам также необходимо затем зарегистрировать этот обработчик событий при настройке фабрики сеансов. Добавьте экземпляр в Configuration.EventListeners.PreUpdateEventListeners
и в Configuration.EventListeners.PreInsertEventListeners
, а затем используйте объект Configuration
при создании фабрики сеансов NHibernate.
Вот что он делает: каждый раз, когда NHibernate вставляет или обновляет объект, он вызывает OnPreInsert()
или OnPreUpdate()
соответственно. Каждый из этих методов, в свою очередь, вызывает CheckDateTimeWithinSqlRange()
.
CheckDateTimeWithinSqlRange()
выполняет итерацию по всем значениям свойств объекта, т. Е. Объекта, который сохраняется. Если значение свойства не равно нулю, проверяется, принадлежит ли оно к типу DateTime
. Если это так, он проверяет, что оно не меньше SqlDateTime.MinValue.Value
(обратите внимание на дополнительный .Value
, чтобы избежать исключений). Нет необходимости проверять SqlDateTime.MaxValue.Value
, если вы используете SQL Server 2012 или новее. Они с радостью примут даже DateTime.MaxValue
, что на несколько тиков больше, чем SqlDateTime.MaxValue.Value
.
Если значение находится за пределами допустимого диапазона, этот код затем выдаст ArgumentOutOfRangeException
с соответствующим сообщением, которое включает имена класса (сущности) и свойства, вызывающего проблему, а также фактическое значение, которое было передано. Сообщение аналогично эквивалентному SqlServerException
для исключения переполнения SqlDateTime, но это упростит выявление проблемы.
Пара вещей, которые следует учесть. Очевидно, это не бесплатно. Вы понесете накладные расходы во время выполнения, поскольку эта логика потребляет ресурсы ЦП. В зависимости от вашего сценария это может не быть проблемой. Если это так, вы также можете рассмотреть возможность оптимизации кода, приведенного в этом примере, чтобы сделать его быстрее. Одним из вариантов, возможно, может быть использование кеширования, чтобы избежать цикла для одного и того же класса. Другой вариант - использовать его только в средах тестирования и разработки. При производстве вы можете быть уверены, что остальная часть системы работает правильно, а значения всегда будут в допустимом диапазоне.
Также имейте в виду, что этот код вводит зависимость от SQL Server. NHibernate обычно используется, чтобы избежать подобных зависимостей. Другие серверы баз данных, поддерживаемые NHibernate, могут иметь другой диапазон допустимых значений для datetime. Опять же, есть варианты решения этой проблемы, например используя разные границы в зависимости от диалекта SQL.
Удачного кодирования!
person
Manfred
schedule
04.07.2017