ユーザーがWindows Formsアプリケーションのデータベースからオブジェクトを編集している場合や、データベースの制約(つまり列の一意の値)に違反する編集を行ったり、エンティティはデータベースに戻り、NHibernateは例外をスローしてセッションを破棄します。NHibernateを使用したWinformでの制約違反の処理
私は、MSDNの記事Building a Desktop To-Do Application with NHibernateで公開されているガイダンスを使用しており、プレゼンター/セッションごとのアプローチを使用しています。プレゼンターの作成時には、私はSessionFactoryへの参照を保持し、新しいセッションを作成し、セッションを介してデータベースからオブジェクトを取り出し、オブジェクト[オブジェクト]への参照を格納します。フォームが表示されると、そのフィールドがオブジェクトから読み込まれます。
フォームに変更が加えられ、ユーザーがデータを保存したい場合、オブジェクトはフィールド値から更新され、オブジェクトはデータベースに再度保存されます。
オブジェクトがデータベースから取得されてから保存されるまでの間にオブジェクトが変更される古い状態の例外を処理します。新しいセッションが作成され、元のオブジェクトが再度ロードされ、紛争が発生し、彼の変更と現在データベースにあるものが提示されます。彼は自分の変更を保存するか、取り消すかを選択できます(そして現在データベースに保存されているものを受け入れます)。制約違反が発生した場合
、2つのいずれかが発生する可能性があることと思われる:
- をオブジェクトは、それが新しいセッションに再ロードすることができ、データベースに変更されていません。
- オブジェクトもデータベースで変更され、最初にロードされたオブジェクトは反映されなくなりました。
しかし、私は私が実際に制約例外は(私はこれをテストした)発生したため、古い状態の例外がスローされることはありませんので、発生した場合に検出することができないと思います。
ケース1は、「FIELD-XはすでにDBに値があります」というエラーメッセージを表示し、実際には何も起こっていないと思われるので、簡単です。ユーザーはFIELD-Xを一意の値に変更し、変更を再入力せずに再度保存することができます。
ケース2を扱うことは、通常の陳腐化状態の例外を処理するようなものです。
私は、元(またはその値)のコピーを保持し、次にフィールドごとに2つのフィールドを比較することで、これを「ブルートフォース」することができます。 しかし、NHibernateを利用することでこれを処理するより良い方法があると思われます。これはどうやって処理しますか?
場合には有用である:
- NHibernateのバージョン:3.2
- オプティロック用いた "ダーティ" 戦略
- の.NET Framework 2.0 SP2
- SQLiteデータベース/ドライバ
EDIT 2月23日 - 私が現在行っていることをよりよく説明するためのサンプルコードをいくつか追加しました。
/**
* Save handler called when user initiates save in UI.
*
* Returns true when save was successful (essentially, tells the presenter
* that the UI can be closed.
*/
private bool SaveData()
{
try
{
if (this.creatingNewUserAccount)
{
// Do whatever is necessary to instantiate a new object.
this.userAccount = new UserAccount();
// and copy values from the UI into the new object.
this.userAccount.Name = this.form.Name;
// etc.
}
else
{
// Just copy values from the UI into the existing object
// from the database.
this.userAccount.Name = this.form.Name;
// etc.
}
using (ITransaction tx = this.session.BeginTransaction())
{
this.accountRepository.store(this.userAccount);
tx.Commit();
}
return true;
}
catch (StaleObjectStateException)
{
HandleStaleStateException();
return false;
}
catch (ArgumentException e)
{
this.m_View.ShowOtherDialog(e.Message);
return false;
}
catch (GenericADOException e)
{
HandleConstraintViolationException();
return false;
}
}
private void HandleStaleStateException()
{
// The session was trashed when the exception was thrown,
// so close it and create a new one.
this.session.Dispose();
this.session = this.sessionFactory.OpenSession();
CurrentSessionContext.Bind(this.session);
// Reload the object from the database.
this.userAccount = LoadData();
// Do a bunch of things that deal with informing the user
// of the stale-state and displaying a form to merge changes.
HandleEditConflict();
}
private void HandleConstraintViolationException()
{
// The session was trashed when the exception was thrown,
// so close it and create a new one.
this.session.Dispose();
this.session = this.sessionFactory.OpenSession();
CurrentSessionContext.Bind(this.session);
// Determine if trying to save a new entity or editing an existing one.
if (this.creatingNewUserAccount)
{
// If saving a new entity, we don't care about the old object
// we created and tried to save.
this.userAccount = null;
}
else
{
// ????
}
}
:私の質問からのコードは、最終的な方法を除いて同じでしたあなたが見るために掲示されたコード - しかし、これは私のためにはうまくいかなかった。 Lock()はエンティティを再接続していないようで、Flush()はDBに再度保存しようとします。しかし、Merge()を使用することの提案は、APIをより深く見て、Refresh()を見つけました。 –