다음과 같이 모두 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
에 인스턴스를 추가 한 다음 NHibernate의 세션 팩토리를 만들 때 Configuration
개체를 사용하십시오.
이것은 무엇을 하는가 : NHibernate가 엔티티를 삽입하거나 업데이트 할 때마다 각각 OnPreInsert()
또는 OnPreUpdate()
을 호출 할 것이다. 이 방법들 각각은 CheckDateTimeWithinSqlRange()
을 호출합니다.
CheckDateTimeWithinSqlRange()
은 개체, 즉 저장되는 개체의 모든 속성 값을 반복합니다. 등록 정보 값이 널이 아닌 경우, 유형이 DateTime
인지 점검합니다. 이 경우 예외가 발생하지 않도록 이상인지 확인합니다 (예외 발생을 방지하려면 추가 .Value
에주의하십시오). SQL Server 2012 이상을 사용하는 경우 SqlDateTime.MaxValue.Value
을 확인할 필요가 없습니다. 그들은 심지어 SqlDateTime.MaxValue.Value
보다 약간 시간 틱 인 DateTime.MaxValue
조차도 기꺼이 받아 들일 것입니다.
값이 허용 범위를 벗어나는 경우이 코드는 전달 된 실제 값뿐만 아니라 문제를 일으키는 클래스 (엔티티) 및 속성의 이름을 포함하는 적절한 메시지와 함께 ArgumentOutOfRangeException
을 던집니다. 이 메시지는 SqlDateTime 오버플로 예외에 해당하는 SqlServerException
과 비슷하지만 문제를 쉽게 찾아 낼 수 있습니다.
몇 가지 고려해야 할 사항이 있습니다. 분명히 이것은 자유롭지 않습니다. 이 로직이 CPU를 소비하므로 런타임 오버 헤드가 발생합니다. 시나리오에 따라 문제가되지 않을 수도 있습니다. 그렇다면이 예제에 제공된 코드를 최적화하여 더 빠르게 만들 것을 고려할 수도 있습니다. 한 옵션은 같은 클래스에 대한 루프를 피하기 위해 캐싱을 사용하는 것일 수 있습니다. 또 다른 옵션은 테스트 및 개발 환경에서만이 옵션을 사용할 수 있습니다. 생산을 위해서는 나머지 시스템이 올바르게 작동하고 값이 항상 유효 범위 내에 있음을 의지 할 수 있습니다.
또한이 코드는 SQL Server에 대한 종속성을 소개합니다. Hibernate는 일반적으로 이와 같은 의존성을 피하기 위해 사용된다. Hibernate에서 지원하는 다른 데이터베이스 서버는 datetime에 대해 허용되는 값의 범위가 다를 수 있습니다. 다시 말하지만이 문제를 해결할 수있는 옵션이 있습니다. SQL 방언에 따라 다른 경계를 사용합니다.
해피 코딩!
nhprof가 없거나 log4net이 없으면 SQL 프로파일 러를 사용할 수 있습니다. – Rippo