4

いくつかのデータソースを介してTDBGridに接続されたメモリ内データセットがあります。問題は、ユーザーが数値フィールドにマイナス記号を入力し、Enterキーを押すか、別のレコードに移動するたびに、AV(検証エラー)が発生することです。TDBGridまたはTClientDataSet(OnEditError、OnPostError)のいずれかからAVをキャッチできません。

私はここで何か間違ったことをしていますか?そうでない場合、誰かがこれを回避することができますか?

Delphi XE2 Enterpriseの使用、4回目の更新。最新のMIDAS.DLL; ターゲットプラットフォーム:Win64。

================================================== ============================

次のことを試してください。

1-新しいVCLアプリケーション/フォームを作成し、オブジェクトTClientDataSet、TDBGrid、およびTDataSourceをフォームに追加します。ClientDataSetはどのデータベースにも接続されていません(メモリ内)

2-永続的な数値フィールドをTable1(TClientDataSet)に追加します。

3-上記の3つのオブジェクトをリンクします。永続フィールドの列がTDBGridに表示されます。

4-メインフォームのOnShowメソッドに、次の行を追加します。

Table1.Active:= False;

Table1.CreateDataSet;

Table1.Active:= True;

5-プログラムを実行し、数値フィールド列にマイナス記号を入力して、Enterキーを押します。

AVを確認してから、OnEditErrorおよび/またはOnPostErrorイベントハンドラーをTable1に追加して、AVをキャッチしてみてください。投稿を中止することはできません。つまり、AVが発生し、Application.OnExceptionハンドラーを使用してのみキャッチできます。これは良くありません。アプリケーションに多くのフォームやルーチンが含まれていると仮定すると、プログラムが実行しているレベルで何かを実行できるはずです。上位レベルではありません。

私の質問をより明確にしたいと思います。

4

1 に答える 1

7

ケンが言うように、サンプルコードなしであなたを助けるのは難しいです。ですから、私はあなたの質問に直接答えることはできませんが、いくつかの指針があれば、あなたはそれを自分で理解することができるかもしれません。だから私はあなたに魚を教えることを試みるつもりです。;)

私はDelphi3および6のTClientDataSetについてかなりの経験がありますが、状況がそれほど変わったとは思えません。さらに、正しい方向にキックスタートするためのテクニックにさらに焦点を当てます。

まず、VCLコードでブレークポイントを取得できるように、必ずDebugDCUでコンパイルしてください。そのコードを読んでステップスルーすることを恐れないでください-それは学ぶための素晴らしい方法です。さらに、Borland / CodeGear/Embarcaderoの醜い栄光の中での間違いを見ることができます。

すでに例外の停止が有効になっていると想定しています(したがって、特にAVエラーが発生していることがわかっているのはなぜですか)。

あなたのテストをしてください。AVを入手すると、おそらくDbClient.pas(またはおそらく下位レベルのユニットの1つ)に表示されます。

コールスタックを調べて、 try..exceptブロックのtry部分内にいる場所を探します。このブロックは、例外を飲み込むか、別のハンドラーに短絡してから飲み込みます。

注:DbClient.pasには、次の不正なコードのかなりの数のインスタンスが散らばっています。

except //swallowing or squashing exceptions is 
end;   //very dangerous and should be avoided.

except
  //short-circuiting to Application.HandleException (usually informs the user)
  //but is not much better. The point is the rest of the program up the 
  //call-stack remains blissfully unaware that something went wrong.
  Application.HandleException(Self);
  Action := raAbort;
end;

サイドコメント:ボーランドがそれをしたからといって、それが良い習慣であるという意味ではありません。あなたと他の多くの人はこれを持っており、これによって燃やされるでしょう。

あなたがそれを見つけたら、あなたはそこの半分にいます。これで、AVについて通知されない理由がわかりました。
これで、その呼び出しスタックの一部を形成するコード内のどこかに、エラーの処理方法の手がかりが得られます。また、VCLコードの少し前にブレークポイントを設定して、例外に至るまでの一連のイベントを確認することもできます。AV(Access Violation)について具体的に言及したので、 nilを参照するオブジェクトを探します。

問題の原因の1つの例は、D5DbClient.pasの次のスニペットにあります。

    FOnReconcileError(DataSet, E, UpdateKind, Action);
  finally
    E.Free;
  end;
except
  Application.HandleException(Self);
  Action := raAbort;
end;

上記から、特定の状況ではOnReconcileErrorイベントハンドラーを実装する必要があることがわかります。これがないと、dllは上記のコードスニペットへのコールバックを与えられず、多くのエラーがカーペットの下で静かに蹴られ、頭を悩ませることになります。

TClientDataSetでの私の経験では、TClientDataSetを「正しく」使用するためにやらなければならない小さな余分なことがいくつかありました。これは、ドキュメントでは特に明白ではありませんでした。やるべきことをすべて理解したら、それらを行う方法の例は少し良くなりました。

残念ながら、私の記憶はこれらすべてのものが何であったかについて少し曖昧です。したがって、これは非常に大まかなガイドであり、おそらくかなり欠落しています。

  • OnReconcileErrorイベントハンドラーの使用を検討してください。
  • UpdateProviderオブジェクトの実装を検討してください。
  • これをメモリ内データセットとして使用するとおっしゃっていたので、おそらくCreateDataSetを正しく使用しています。しかし、私が正しく覚えていれば、フィールドを指定するための2つのオプションがあり、より細かい詳細属性のいくつかは少し厄介でした。
  • 無関係なヒント:文字列フィールドの長さに注意してください。TClientDataSetは、短い文字列と同様の方法でメモリを群がらせていました(そして今でもそうなる可能性があります)。

編集

質問の追加情報に基づいて、上記の手法を使用すると、次のメソッドからEDatabaseErrorがスローされます。

procedure TIntegerField.SetAsString(const Value: string);
var
  E: Integer;
  L: Longint;
begin
  if Value = '' then Clear else
  begin
    Val(Value, L, E);
    if E <> 0 then DatabaseErrorFmt(SInvalidIntegerValue, [Value, DisplayName]);
    SetAsInteger(L);
  end;
end;

呼び出しスタックの上位にあると、問題を解決する方法を示唆する次のメソッドが得られます。

procedure TField.SetEditText(const Value: string);
begin
  if Assigned(FOnSetText) then FOnSetText(Self, Value) else SetText(Value);
end;

また、KeyPressまでのコールスタックをたどると、データセットやグリッドコードがまったく含まれていないことに気付くでしょう。

OnPostエラーのエラーを処理しようとしても機能しなかった理由は、レコードを投稿する試みに近づいていないためです。フィールド検証エラーが発生し、それを処理する場所がフィールド上にあります。

エラーを解決する方法のヒントに戻ると、フィールドにOnSetTextイベントを実装する必要があります。

ただし、そうしないことをお勧めします。すぐに使用できる動作は完全に受け入れられます。ユーザーは、何が間違っていたかを説明するエラーメッセージを受け取り、それを修正して再試行する機会を得ます。気が変わったら、Escapeキーを押して編集をキャンセルできます。

于 2012-06-06T18:31:00.237 に答える