2009-05-03 12 views
2

写真とタグの間に多対多の関係があります。写真は複数のタグを持ち、複数の写真は同じタグを共有できます。NHibernateで多対多の関係で使用するアルゴリズム

ディレクトリ内の写真をスキャンしてNHibernateに追加するループがあります。そのプロセス中にいくつかのタグが写真に追加されます。 2009年に撮影された2009年のタグ。

タグクラスは、EqualsとGetHashCodeを実装し、唯一の署名プロパティとしてNameプロパティを使用します。写真とタグの両方に代理キーがあり、バージョンが変更されています。

public void Import() { 
    ... 
    foreach (var fileName in fileNames) { 
     var photo = new Photo { FileName = fileName }; 
     AddDefaultTags(_session, photo, fileName); 
     _session.Save(photo); 
    } 
    ... 
} 

private void AddDefaultTags(…) { 
    ... 
    var tag =_session.CreateCriteria(typeof(Tag)) 
        .Add(Restriction.Eq(“Name”, year.ToString())) 
        .UniqueResult<Tag>(); 

    if (tag != null) { 
     photo.AddTag(tag); 
    } else { 
     var tag = new Tag { Name = year.ToString()) }; 
     _session.Save(tag); 
     photo.AddTag(tag); 
    } 
} 

私の問題があるタグが存在しない場合、例えば:

私は、次のようないくつかのコードを持っています新年の最初の写真。 AddDefaultTagsメソッドは、タグがデータベースに存在するかどうかを確認し、それを作成してNHibernateに追加します。 1枚の写真を追加するときにはうまくいくが、新年に複数の写真をインポートして同じ作業単位内でインポートすると、データベースにはまだ存在せず、再び追加されるので失敗する。作業ユニットを完了すると、同じ名前のタグテーブルに2つのエントリを追加しようとするので失敗します...

私の質問は、データベース内に単一のタグを作成しようとするだけです上記の状況。新しく追加されたタグのリストを自分で管理する必要がありますか、それともマッピングを設定して動作させることはできますか?

答えて

2

条件が古いデータを返さないようにするには、_session.Flush()を実行する必要があります。 または、_session.FlushModeをAutoに設定することで正しく行うことができます。

FlushMode.Autoを使用すると、条件が実行される前にセッションが自動的にフラッシュされます。

EDIT:重要!あなたが示したコードを読んでいるとき、あなたの作業ユニットにトランザクションを使用しているようには見えません。トランザクションで作業単位をラップすることをお勧めします.NH2.0 +を使用している場合は、FlushMode.Autoが動作するために必要です。さらにここで

読むには:NHibernate ISession Flush: Where and when to use it, and why?

+0

私はその部分をうまく説明しなかったことを申し訳なく思っています。私のFlushModeはNeverに設定されています。私は作業が完了したら明示的にフラッシュする作業単位内で作業しています。ああ、トランザクション内でも動いている。 – HakonB

+0

ああ。 OK。新しいタグを挿入するコードのポイントでDBへの呼び出しに絶対に反対する場合、私はtheresがあなた自身のタグを追跡する以外の方法ではないと信じています。しかし、なぜそれをフラッシュしないのかわかりませんが、作業単位内の例外をロールバックすることはできます。 – asgerhallas

+0

ロールバックの良さと注意点については、ここをクリックしてください:http://objectissues.blogspot.com/2004/12/understanding-hibernate-transaction.html – asgerhallas

0

新しいタグを保存した後にトランザクションをコミットする必要があるたびに、新しいタグをチェックするときにデータベースに追加する場合は、

写真を処理する前に、タグをコレクションに読み込む方法もあります。 あなたが言ったように、あなたはローカルを検索し、必要に応じて新しいタグを追加するでしょう。フォルダを終了すると、セッションをコミットできます。

私はあなたの質問を正しく解釈していないかもしれないので、マッピングを投稿してください。

+0

私の問題は、作業単位としてインポートを実行し、ユニット全体が完了する前にデータベースに何もコミットしないことです。理想的には、作業単位で実行することは、AddDefaultTagsメソッドに対して透過的でなければなりませんが、それは可能ではないと思います... 私はあなたが私の問題をちゃんと理解していると思います。マッピング自体は問題ではないと思います。マッピングを使用する正しいパターンを決定することです。 – HakonB

0

これは典型的な問題「が存在しない何かをロックする」ということです。私はすでに数回それに直面していましたが、それでも簡単な解決策はありません。

これは私が今まで知っているオプションは次のとおりです。

楽観
  • :名前に一意の制約があり、セッションの1つは、コミット時に投げてみましょう。その後、もう一度試してみてください。別のエラーが発生した場合、無限ループで終了しないようにする必要があります。
  • ペシミスティック:新しいタグを追加すると、TSQLを使用してタグテーブル全体がロックされます。
  • .NET Locking:.NETロックを使用してスレッドを同期させます。これは、並列トランザクションが同じプロセス内にある場合にのみ機能します。

(怒鳴る参照)独自のセッションを使用してタグを作成します。

public static Tag CreateTag(string name) 
{ 
    try 
    { 
    using (ISession session = factors.CreateSession()) 
    { 
     session.BeginTransaction(); 
     Tag existingTag = session.CreateCriteria(typeof(Tag)) /* .... */ 
     if (existingtag != null) return existingTag; 
     { 
     session.Save(new Tag(name)); 
     } 
     session.Transaction.Commit(); 
    } 
    } 
    // catch the unique constraint exception you get 
    catch (WhatEverException ex) 
    { 
    // try again 
    return CreateTag(name); 
    } 
} 

これは簡単なように見えますが、いくつかの問題があります。あなたは常に存在するか作成された(そしてすぐにコミットされる)タグを取得します。しかし、あなたが得るタグは別のセッションから来たものなので、あなたのメインセッションでは切り離されています。カスケード(おそらく望んでいない)や更新を使用してセッションに接続する必要があります。

タグの作成は、もはやメイントランザクションに結合されていません。これが目標ですが、トランザクションをロールバックするとデータベース内に作成されたすべてのタグが残されます。つまり、タグを作成することは、トランザクションの一部ではありません。

関連する問題