2

写真とタグの間に多対多の関係があります。写真には複数のタグを付けることができ、複数の写真で同じタグを共有できます。

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

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

次のようなコードがあります。

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 つのエントリを追加しようとするため失敗します...

私の質問は、NHibernate が上記の状況でデータベースに単一のタグのみを作成しようとすることを確認する方法です。新しく追加されたタグのリストを自分で維持する必要がありますか、それとも機能するようにマッピングを設定できますか?

4

3 に答える 3

2

_session.Flush()基準が古いデータを返すべきでない場合は、実行する必要があります。_session.FlushModeまたは、を Autoに設定することで、正しく実行できるはずです。

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

編集:そして重要です!表示されたコードを読むと、作業単位にトランザクションを使用しているようには見えません。作業単位をトランザクションでラップすることをお勧めします - NH2.0+ を使用している場合、これは FlushMode.Auto が機能するために必要です!

詳細はこちら: NHibernate ISession Flush: どこで、いつ、なぜ使用するのか?

于 2009-05-03T19:32:49.980 に答える
0

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

これは私が今まで知っているオプションです:

  • 楽観的: 名前に一意の制約を設定し、セッションの 1 つがコミット時にスローされるようにします。その後、もう一度試してください。別のエラーが発生したときに無限ループに陥らないようにする必要があります。
  • 悲観的: 新しいタグを追加すると、TSQL を使用してタグ テーブル全体がロックされます。
  • .NET ロック: .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);
  }
}

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

タグの作成は、メインのトランザクションに関連付けられなくなりました。これが目標でしたが、トランザクションをロールバックすると、作成されたすべてのタグがデータベースに残されることも意味します。つまり、タグの作成はトランザクションの一部ではなくなりました。

于 2009-05-04T08:49:04.013 に答える
0

毎回チェックするときに新しいタグをデータベースに入れたい場合は、保存してそこに置くためにトランザクションをコミットする必要があります。

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

質問を正しく解釈していない可能性があるため、マッピングを投稿する必要があります。

于 2009-05-03T19:25:14.507 に答える