2011-01-31 8 views
7

再生が困難な散発的なエラーが発生しています。私の最初の推測は、どういうわけか、漏れているnhibernateセッションを持っているということです。しかし、私がnhibernate profilerを実行したとき、私は普通のことをほとんど見かけませんでした。NHibernate:System.Argument例外:同じキーを持つ項目がすでに追加されています

  • MVC 2.0
  • 流暢バージョン1.1.0.685
  • NHibernateのバージョン2.1.2.4000

例外:System.ArgumentExceptionの:同じキーを持つ アイテムが既に 追加されています。

スタックトレース: System.Collections.Generic.Dictionary 2.Insert(TKey key, TValue value, Boolean add) at NHibernate.Util.ThreadSafeDictionary 2.Add(処理鍵 キー、TValue値)で でNHibernate.SqlTypes.SqlTypeFactory.GetTypeWithLen [T](のInt32 長、TypeWithLenCreateDelegate createDelegate)で NHibernate.Type.EnumStringType..ctor(タイプ enumClass、のInt32長)私は、リポジトリのモデルを使用しています

。ここに私のリポジトリクラスがあります。

public sealed class Repository<T> : IRepository<T> where T : CoreObjectBase 
{ 
    #region IRepository<T> Members 

    private ISession Session 
    { 
     get 
     { 
      return new SessionHelper().GetSession(); 
     } 
    } 

    public IQueryable<T> GetAll() 
    { 
     return (from entity in Session.Linq<T>() select entity); 
    } 

    public T GetById(int id) 
    { 
     return Session.Get<T>(id); 
    } 

    public void Save(params T[] entities) 
    { 
     using (ITransaction tx = Session.BeginTransaction()) 
     { 
      for (int x = 0; x < entities.Count(); x++) 
      { 
       var entity = entities[x]; 

       entity.Validate(); 

       Session.SaveOrUpdate(entities[x]); 

       if (x == entities.Count() - 1 || (x != 0 && x % 20 == 0)) //20 is the batch size 
       { 
        Session.Flush(); 
        Session.Clear(); 
       } 
      } 
      tx.Commit(); 
     } 
    } 

    public void SaveWithDependence<K>(T entity, K dependant) where K : CoreObjectBase 
    { 
     entity.Validate(); 
     dependant.Validate(); 

     using (ITransaction tx = Session.BeginTransaction()) 
     { 
      Session.SaveOrUpdate(entity); 
      Session.SaveOrUpdate(dependant); 
      tx.Commit(); 
     } 
    } 

    public void Save(T entity) 
    { 
     entity.Validate(); 

     using (ITransaction tx = Session.BeginTransaction()) 
     { 
      Session.SaveOrUpdate(entity); 
      tx.Commit(); 
     } 
    } 

    public void Delete(T entity) 
    { 
     using (ITransaction tx = Session.BeginTransaction()) 
     { 
      Session.Delete(entity); 
      tx.Commit(); 
     } 
    } 

    public T GetOne(QueryBase<T> query) 
    { 
     var result = query.SatisfyingElementFrom(Session.Linq<T>()); 

     return result; 

     //return query.SatisfyingElementFrom(Session.Linq<T>()); 
    } 

    public IQueryable<T> GetList(QueryBase<T> query) 
    { 
     return query.SatisfyingElementsFrom(Session.Linq<T>()); 
    } 

    /// <summary> 
    /// remove the sepcific object from level 1 cache so it can be refreshed from the database 
    /// </summary> 
    /// <param name="entity"></param> 
    public void Evict(T entity) 
    { 
     Session.Evict(entity); 
    } 
    #endregion 
} 

ここで私のセッションヘルパーはthisに適合しています。

public sealed class SessionHelper 
{ 
    private static ISessionFactory _sessionFactory; 
    private static ISession _currentSession; 

    public ISession GetSession() 
    { 
     ISessionFactory factory = getSessionFactory(); 
     ISession session = getExistingOrNewSession(factory); 
     return session; 
    } 

    private ISessionFactory getSessionFactory() 
    { 
     if (_sessionFactory == null) 
     { 
      _sessionFactory = BuildSessionFactory(); 
     } 

     return _sessionFactory; 
    } 

    private ISessionFactory BuildSessionFactory() 
    { 
     return Fluently.Configure().Database(
      FluentNHibernate.Cfg.Db.MsSqlConfiguration.MsSql2005 
       .ConnectionString(c => c 
        .FromConnectionStringWithKey("MyDatabase")) 
        .AdoNetBatchSize(20)) 
       .Mappings(m => m.FluentMappings.AddFromAssemblyOf<SessionHelper>()) 
       .BuildSessionFactory(); 
    } 

    private ISession getExistingOrNewSession(ISessionFactory factory) 
    { 
     if (HttpContext.Current != null) 
     { 
      ISession session = GetExistingWebSession(); 
      if (session == null) 
      { 
       session = openSessionAndAddToContext(factory); 
      } 
      else if (!session.IsOpen) 
      { 
       session = openSessionAndAddToContext(factory); 
      } 

      return session; 
     } 

     if (_currentSession == null) 
     { 
      _currentSession = factory.OpenSession(); 
     } 
     else if (!_currentSession.IsOpen) 
     { 
      _currentSession = factory.OpenSession(); 
     } 

     return _currentSession; 
    } 

    public ISession GetExistingWebSession() 
    { 
     return HttpContext.Current.Items[GetType().FullName] as ISession; 
    } 

    private ISession openSessionAndAddToContext(ISessionFactory factory) 
    { 
     ISession session = factory.OpenSession(); 
     HttpContext.Current.Items.Remove(GetType().FullName); 
     HttpContext.Current.Items.Add(GetType().FullName, session); 
     return session; 
    } 
} 

この問題を回避するためのアイデアや提案はありますか?

+0

Josh、あなたはSessionHelperスレッドを安全にしますか? –

答えて

2

問題は、SessionHelperはスレッドセーフではありません。潜在的にいくつかのセッションファクトリをビルドすることになります(これはSingletonの実装が悪いので、あなたが見ているエラーを引き起こす可能性があります)。

代わりにSharpArchitectureを使用することをおすすめします。

+0

私は本当に私のプロジェクトに別のフレームワークをインポートする必要はありません。私の設定は、Webプロジェクトがnhibernateやnhibernateを参照するライブラリについての知識がなく、そのようにしたいのです。 MVC App - >サービスレイヤ - >リポジトリレイヤ - > SessionHelper。これは、Windowsアプリケーションがサービスレイヤをも使用するWebサービスを介して接続するため、このように設計されています。 – Josh

+1

@ Josh:フレームワーク全体を購入する必要はなく、コードをブラウズしてそのアイデアを使用することができます。 –

+1

SharpArchitectureがなくなった – madth3

1

nhibernate設定を構成しているときに同じ問題が発生しました。同じキーを持つ項目が既に追加されています。

私にとっては、2つのスレッドが、異なるデータベースに接続するための異なる構成をプログラムで構築していたことがありました。

コンフィギュレーションメーカー全体にロックを追加して、問題が解決しました。

私は、構成オブジェクトが内部的なグローバル状態に依存していると推測しています。つまり、構成自体がシングルトンであると仮定しています(プログラムによって構築されたものとは完全にファイル駆動の場合)。

1

これは古い質問であることがわかりましたが、NHibernate 3.0を使用してわずか数日前に同様のエラーが発生しました。

この問題が発生する可能性のある読者のために、これはNHibernateの古いバージョンで既知のスレッドセーフティの問題の結果です。これはバージョン3.2で修正されましたが、古いバージョンでは修正されず、この問題が発生する可能性があります。このバグのエントリで問題を説明しています:https://nhibernate.jira.com/browse/NH-3271

関連する問題