2011-02-01 8 views
7

例外の理由を知っています(SqlDateTimeオーバーフローが発生しました)。1/1/1753 12:00:00 AMと12/31/9999 11:59:59の間にある必要があります。 PM。)は、エンティティ内のnull可能ではないDateTimeフィールドであるため、NhibernateはMSSQLが受け入れるよりも小さいDateTime値を保存したい。Nhibernate:SqlDateTimeオーバーフロー例外の責任フィールドを見つける方法

問題は、正しいDateTimeフィールドを見つけるために、プロジェクトには数多くのエンティティがあることです。

SaveOrUpdate()の後に例外が発生しますが、保存したいエンティティによってトリガされませんが、現在のセッションで読み込まれ、flush()の影響を受ける他のエンティティがトリガされます。

本当にどのフィールドが例外の原因であるかを知ることができますか?

+1

nhprofを使用していないかlog4netを使用している場合、SQLプロファイラを使用することができます。 – Rippo

答えて

0

あなたは生成されたsqlを出力し、不正なDateTimeのためにそれを見直そうとしましたか? NHProfilerのようなものを使用した方が簡単です(私はそれらのためにはうまくいきません)。しかし、本当にあなたのためにやっていることは、SQLを表示/分離しています。少し余計な努力を払ってトリックは、本当に深いセーブであれば、読めるSQLがたくさんある可能性がありますが、かなり早くそれを見つけられる可能性があります。

+0

SQLプロファイラがクエリを表示しないため、Nhibernateはアプリケーションを実際にシャットダウンすると推測しますステートメントを発射しますか?私が見つけることができるクエリは "?"プレースホルダーので、私は偽の値が設定されているか分からない。 – Tardis

+0

Bummer。 Lemmeは他のオプションをいくつか見てみましょう。問題を押すのではなく、NHProfがその詳細を表示します。試用版を引き下げる価値があるかもしれません。あなたのアプリケーションでの設定はかなりシンプルです。 – nkirkes

+0

param値が表示されていなくても、見つけられたSQLを投稿できますか? – nkirkes

3

例外をSqlTypeExceptionにキャストすると、Dataコレクションが公開されます。通常、コレクションには単一のKeyと単一のValueがあります。値は実行しようとしたSQLです。 DMLを調べることで、どのテーブルが操作されているかを確認することができます。そのテーブルが狭いので、問題のあるコラムを簡単に判断できます。

ここでは、例外のKeyとValueを吐き出すために使用する簡単なコードを示します。

  catch (SqlTypeException e) 
      { 
       foreach(var key in e.Data.Keys) 
       { 
        System.Console.Write("Key is " + key.ToString()); 
       } 
       foreach(var value in e.Data.Values) 
       { 
        Console.WriteLine("Value is "+value.ToString()); 
       } 
      } 
+2

これはNH 3.1で私にとってはうまくいきませんでした。 –

+5

理論的にはこれは私にとっては効果的ですが、データ収集には値がないようです。その他の提案はありますか? – Aries51

0

あなたは次のようにIPreUpdateEventListenerIPreInsertEventListenerの両方を実装するクラスを作成することができます。

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.PreUpdateEventListenersConfiguration.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の方言に応じて異なる境界を使用します。

ハッピーコーディング!

関連する問題