あなたは次のように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
かどうかをチェックします。その場合は、SqlDateTime.MinValue.Value
以上であることがチェックされます(例外を回避するには、追加の.Value
に注意してください)。 SQL Server 2012以降を使用している場合は、SqlDateTime.MaxValue.Value
と照合する必要はありません。彼らはSqlDateTime.MaxValue.Value
より大きい数ティックであるDateTime.MaxValue
さえもうれしく受け入れます。
値が許容範囲外の場合、このコードは、問題の原因となったクラス(エンティティ)およびプロパティの名前と、渡された実際の値を含む適切なメッセージを含むArgumentOutOfRangeException
をスローします。メッセージはSqlDateTimeオーバーフロー例外の同等のSqlServerException
に似ていますが、問題の特定が容易になります。
いくつか考慮すべき点があります。明らかに、これは無料ではありません。このロジックがCPUを消費するため、ランタイムオーバーヘッドが発生します。あなたのシナリオに応じて、これは問題ではないかもしれません。そうであれば、この例のコードを最適化してより高速にすることも検討できます。 1つのオプションは、キャッシュを使用して同じクラスのループを回避することです。もう1つの選択肢は、テストおよび開発環境でのみ使用することです。プロダクションでは、システムの残りの部分が正しく動作し、値が常に有効な範囲内にあることに頼ることができます。
また、このコードではSQL Serverへの依存性が導入されていることに注意してください。 NHibernateは、通常このような依存関係を避けるために使用されます。 NHibernateでサポートされている他のデータベースサーバでは、datetimeの許容範囲が異なる可能性があります。ここでも、これを解決するためのオプションがあります。 SQLの方言に応じて異なる境界を使用します。
ハッピーコーディング!
nhprofを使用していないかlog4netを使用している場合、SQLプロファイラを使用することができます。 – Rippo