2011-01-31 15 views
7

Recibo un error esporádico que es difícil de reproducir. Mi primera suposición es que de alguna manera tengo una sesión de nhibernate con fugas, sin embargo, cuando ejecuté el nhibernate profiler, no vi nada fuera de lo común.NHibernate: excepción de System.Argument: un elemento con la misma clave ya se ha agregado

  • MVC 2,0
  • versión Fluido versión 1.1.0.685
  • NHibernate 2.1.2.4000

Excepción: System.ArgumentException: un elemento con la misma clave ya ha sido añadido .

Seguimiento de la pila: en System.Collections.Generic.Dictionary 2.Insert(TKey key, TValue value, Boolean add) at NHibernate.Util.ThreadSafeDictionary 2.Add (TKey clave, valor TValue) en NHibernate.SqlTypes.SqlTypeFactory.GetTypeWithLen [T] (Int32 longitud, TypeWithLenCreateDelegate CreateDelegate) a NHibernate.Type.EnumStringType..ctor (Tipo enumClass, longitud Int32)

estoy usando un modelo de repositorio. Aquí está mi clase de repositorio.

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 
} 

Y aquí está mi sesión de ayudante, adaptación de 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; 
    } 
} 

¿Alguna idea o sugerencia para evitar este problema?

+0

Josh, ¿hace que su thread SessionHelper sea seguro? –

Respuesta

2

El problema es que SessionHelper no es seguro para subprocesos. Posiblemente construirá varias fábricas de sesión (es una mala implementación de Singleton), lo que a su vez probablemente cause el error que estás viendo.

Recomiendo usar SharpArchitecture como guía.

+0

Realmente no quiero tener que importar otro marco en mi proyecto. Mi configuración es tal que mi proyecto web no tiene conocimiento de nhibernate o de la biblioteca que hace referencia a nhibernate, y me gustaría mantenerlo de esa manera. MVC App -> Service layer -> Repository Layer -> SessionHelper. Está diseñado de esta manera a propósito porque una aplicación de Windows se conecta a través de un servicio web que también utiliza la capa de servicio. – Josh

+1

@Josh: no tiene que comprar todo el marco, puede navegar por el código y usar sus ideas. –

+1

SharpArchitecture se ha ido – madth3

1

Me encontré con el mismo problema, "Ya se ha agregado un elemento con la misma clave" al construir la configuración nhibernate.

Lo que estaba sucediendo para mí era que dos subprocesos construían de forma programática configuraciones diferentes, destinadas a conectarse a diferentes bases de datos, al mismo tiempo.

Agregué un candado alrededor de todo el fabricante de la configuración, y el problema desapareció.

así que supongo que el objeto de configuración depende de un estado mundial interna, es decir, asume que la configuración en sí es un producto único (como supongo que sería, si estuviera totalmente impulsado por presentar, en lugar de construir mediante programación).

1

Me doy cuenta de que esta es una vieja pregunta pero tuve un error similar hace unos días, usando NHibernate 3.0.

Para los lectores que puedan tropezar con este problema: este es el resultado de un problema conocido de seguridad de subprocesos en versiones anteriores de NHibernate. Esto se solucionó en la versión 3.2, pero las versiones anteriores no tendrán la solución y pueden producir este problema.Esta entrada de error describe el problema: https://nhibernate.jira.com/browse/NH-3271

Cuestiones relacionadas