あなたが説明しているケースは、トランザクションをほとんど求めているだけだと思います。
適切なデータベース エンジンを使用している場合、ADO.Net はそれらを簡単に処理します (つまり、たとえば SqlServerCE はありません:))。
たとえば、TransactionScopeクラスを参照してください。データベースと対話する前にそのようなオブジェクトを構築し、Complete() を呼び出した場合にのみ、変更が「コミット」されます。そのままにしておくか、Dispose() すると、トランザクションがキャンセルされ、DB のすべての変更が「ロールバック」されるため、元に戻します。
したがって、あなたの場合、フォームのctorまたはonLoaded()でトランザクションを開き、「保存」でComplete()を開き、他のウィンドウを閉じるときにDispose()を開きます。
これは小規模なシステム、特にシングルユーザーのシステムでこのような処理を行う通常の方法ですが、注意してください: システムが多数の同時ユーザーを処理する必要がある場合、この方法では使用できない場合があります。トランザクションは、完了するかキャンセルされるまで行とテーブルをブロックするため、「他のユーザー」には大きな遅延が発生する可能性があります..
では、何人のユーザーをサポートする必要があり、同じ内容をどのくらいの頻度で編集しようとするのでしょうか?
-- 編集: (10 ユーザー)
多くのユーザーがいる場合、長時間実行されるトランザクションは避けたいと思うでしょう。フォームの読み込み時にトランザクションを開くことは受け入れられず、現在の 1 人のユーザーがウィンドウを閉じるまで、多くのユーザーがロックされます。ただし、すべての変更を 1 つのバッチでプッシュする Save() でトランザクションを使用することは問題ありません。
もちろん、トランザクションをまったくなくすことができれば、それはすばらしいことです。しかし、データの整合性を維持する必要がある場合、これを行うのは非常に困難です。トランザクションの必要性をなくすには、ほとんどの場合、DB 側のデータ構造と、データを取得して操作する方法の両方を再設計する必要があります。データ。両方を再設計したい場合は、まず既存のデータ アクセス フレームワークを使用するように再設計することをお勧めします。基本的な .Net ADO でさえ、SqlClient 準拠のデータベースに保持されているデータベースをオンラインで編集するための非常に優れた機能を備えているからです。 .
したがって、ほとんどのコードを書き直したり再考したりしたくない場合は、データをバッファリングし、データベースでの実際のすべての操作を遅らせるだけで済みます。
「単純な」フォームで実行したい場合があります。フォームを表示するときは、フォームをデータベース駆動型データソースに直接バインドするのではなく、必要なすべてのデータを BindingList<>、DataTable などにダウンロードします。お気に入り。代わりに、フォームをそれらにバインドします。おそらく、あなたはすでにそのようなものを設定しています。ただし、重要なことは、これらのデータコンテナはすべてオフラインにするか、少なくともreadonly+delayloadedにする必要があるということです。
次に、ユーザーが UI で実行するすべての操作をインターセプトする必要があります。アプリケーションが動作すると仮定しているので、確かに既に完了しています:) フォームはオフラインでキャッシュされたアイテムにバインドされているため、アプリケーションはそのキャッシュされたデータに対して操作を実行し、データベースにはまったく触れないでください。しかし、それだけではありません。キャッシュされたデータに対してそれらを実行することに加えて、どのテーブルに何が起こったのかを記録する必要があります。
その後、最終的にユーザーが遊んでいるのをやめて CANCEL を押すと:) - すべてを破棄してフォームを閉じるだけです。データベースは変更されません。
保存時 - 新しいトランザクションを開き、変更のリストを繰り返し処理し、データベースでレコーダーの変更を効果的に再生してから、トランザクションをコミットします。
ただし、次の 2 つの点に注意してください。ユーザーがデータをキャッシュしたときと [保存] を押したときに、データベースが変更された可能性があります。これを検出して中止するか、競合を解決する必要があります。記録された変更の実行中または実行前に、そのトランザクション内でそれを行う必要があります。オンライン データとオフライン キャッシュ データ (変更されていない元の値であり、ユーザーによって変更されたものではない) を比較するだけで検出することも、OptimisticLockingなどの他のメカニズムを使用して行のバージョン タグを比較することもできます。
記録再生が気に入らない場合は、変更されたオフライン データを取得し、それを現在のオンライン テーブルと一般的な方法で比較する "DIFF" ユーティリティを実装できます。これはやや難しいですが、おまけがあります。このようなユーティリティを使用すると、最初にデータを二重にキャッシュできます。1 つのコピーはオフライン参照用 (保存されたばかりで、ユーザーが操作することはありません)、もう 1 つのコピーはオフライン編集用 (すべてフォーム)。ここで、保存時にトランザクションを開き、参照データをオンライン データベースと比較します。違いがある場合は、衝突を検出したことになります。解決/マージ/中止など。違いがない場合は、変更されたデータをオンライン データと比較し、見つかったすべての違いをデータベースに適用してトランザクションをコミットします。
どちらの方法にも長所と短所があります。実装の難しさは別として、キャッシングのメモリの問題、大きすぎるテーブルをあえてコピーする場合の遅延の問題などがあります。
しかし、一度解決すると、かなりうまくいくでしょう。
終了したら、DataSet+DataTable の小さな sis を実装したことを自慢できます。私は冗談を言っていませんし、あなたを笑っていません。私は、DAO レイヤーを再構築し、プラットフォームの設計者/開発者によって既に行われた大変な作業を理解して使用するように誰もがあなたに言っている理由をお見せしようとしています :)
とにかく、データ構造を再考すれば、衝突やトランザクションをまったく回避できると言いました..たとえば、なぜ行を削除するのですか? SQL に気の利いた DELETE ステートメントがあることは知っていますが、その行を本当に削除する必要があるのでしょうか。「bool isDeleted」列を追加して、ユーザーがグリッドから行を削除したときに、その行セルを True に設定し、アプリケーションで isDeleted=true 行を除外して表示しないようにすることはできませんか? ビューや集計に含めませんか? おまけ: sys/db 管理者は魔法のツールを利用できるようになりました: undelete..
さらに考えてみましょう: 行を更新する必要がありますか? たぶん、(この日付) からその行に新しい価格が必要であるという情報を追加することができますか? もちろん、構造を大幅に変更する必要があります。エンティティにはプロパティがありませんが、タイムスタンプ付きのプロパティ変更のログがあり (または行にバージョン番号があり、複製する必要があります..)、クエリは最新バージョンのデータに対してのみ実行する必要があります。など。長所: データベースは追加専用になりました。トランザクションが必要な場合でも、非常に短時間です。短所: SELECT クエリは複雑で、特に多くのテーブルを結合する場合は遅くなる可能性があります..
長所/短所: そして、あなたのデータベースは実際にはデータベースではなく非常にメタ的に見え始めます...
短所: 既存のアプリケーションをそのようなデータベース構造に「アップグレード」するのは非常に難しい作業です。新しいアプリを最初から作成し、odl システムからデータをインポートする方が、数倍速くなる場合があります。
さて、要約すると:
- 記載されている方法のいずれもお勧めしません。
- まず、NHibernate、EntityFramework、DevExpress の XPO などの ORM フレームワークを使用することをお勧めします。それらのいずれかがあなたの時間を大幅に節約します。ここに挙げた 3 つには、OptimisticLocking 衝突検出機能も組み込まれています。そのようなツールが存在するのに、なぜ SQL 自作フレームワークを使用するのでしょうか?
- そうでない場合は、次に、フレームワークにある既存のツールを使用することをお勧めします。SqlClient を使用しているのに、なぜ DataSet と DataTables を使用しないのですか? これらは SqlClient と共に提供され、多くの便利なメカニズムが組み込まれています。それ以外の場合は、すべて自分で実装およびテストするのに何週間も費やすことになります。DataSet とその衝突検出、およびそのマージ アルゴリズムの使用方法を学び、それらを使用します。実験と学習に少し時間を費やすことになりますが、車輪の再発明をしないことで膨大な時間を節約できます。
- 本当に手動で行いたい場合は、データのキャッシュと記録の再生から始めてください。理解しやすく、現在プレーンな SQL クエリを使用している場所ならどこでも導入するのは非常に簡単で、あらゆる種類のキャッシュ同期とバージョン チェックの問題をすぐに紹介できます。上記のフレームワークのメカニズムが実装され、それらがどのように機能し、どのような長所/短所があるか。
- そして、二重キャッシュの差分アプローチについて.. その記録返済を書きたくなるかもしれませんが、衝突を検出/解決/マージする方法をよく知っている場合にのみ使用してください。試す前に、少なくとも 1 つの記録再生アプローチを実装してください。
..そしてもちろん、長期的なトランザクションを使用することもできます。導入するのはばかげて簡単で、ユーザーを「苛立たせるだけ」です.. まあ、または、90% を超えるユーザーが常に衝突してロックにぶつかると、システムが使用できなくなることさえあります..いいえ、それは冗談でした. 長期的なトランザクションを使用しないでください。1 ~ 4 人のユーザー、または非常にまばらなデータベースの場合は問題ありません。