NHibernate: System.Argument Exception: элемент с таким же ключом уже добавлен - PullRequest
7 голосов
/ 31 января 2011

Я получаю спорадическую ошибку, которую трудно воспроизвести. Мое первое предположение состоит в том, что каким-то образом у меня протекает сеанс nhibernate, однако, когда я запустил профилировщик nhibernate , я не увидел ничего необычного.

  • 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 (TKey ключ, значение TValue) в NHibernate.SqlTypes.SqlTypeFactory.GetTypeWithLen [Т] (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;
    }
}

Есть идеи или предложения, чтобы избежать этой проблемы?

Ответы [ 3 ]

2 голосов
/ 31 января 2011

Проблема в том, что SessionHelper не является поточно-ориентированным. Он потенциально может построить несколько фабрик сессий (это плохая реализация Singleton), что, в свою очередь, может вызвать ошибку, которую вы видите.

Я рекомендую использовать SharpArchitecture вместо руководства.

1 голос
/ 09 сентября 2015

Я понимаю, что это старый вопрос, но у меня была похожая ошибка всего несколько дней назад при использовании NHibernate 3.0.

Для читателей, которые могут наткнуться на эту проблему: это результат известного потокапроблема безопасности в старых версиях NHibernate.Это было исправлено в версии 3.2, но более старые версии не будут иметь исправления и могут вызвать эту проблему.Эта запись об ошибке описывает проблему: https://nhibernate.jira.com/browse/NH-3271

1 голос
/ 02 ноября 2012

Я столкнулся с той же проблемой: «Элемент с тем же ключом уже добавлен» при создании конфигурации nhibernate.

Для меня произошло то, что два потока программно создавали разные конфигурации, предназначенные для одновременного подключения к разным базам данных.

Я добавил блокировку для всего создателя конфигурации, и проблема исчезла.

Таким образом, я предполагаю, что объект конфигурации зависит от некоторого внутреннего глобального состояния, то есть предполагает, что сама конфигурация является одноэлементной (как я предполагаю, если бы она была полностью управляемой файлами, а не программно построенной).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...