我收到了一个难以重现的零星错误。我的第一个猜测是,我有一个泄漏的nhibernate会话,但是当我运行nhibernate profiler时,我没有看到太多与众不同的。
异常:System.ArgumentException: 具有相同键的项目已经存在 已被添加。
Stack Trace:at System.Collections.Generic.Dictionary
2.Insert(TKey key, TValue value, Boolean add) at NHibernate.Util.ThreadSafeDictionary
2.添加(TKEY的 关键,TValue价值)at NHibernate.SqlTypes.SqlTypeFactory.GetTypeWithLen [T](的Int32 length,TypeWithLenCreateDelegate createDelegate)at NHibernate.Type.EnumStringType..ctor(类型 enumClass,Int32 length)
我正在使用存储库模型。这是我的存储库类。
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 :(得分:2)
问题是,SessionHelper
不是线程安全的。它可能会构建几个会话工厂(这是一个不好的Singleton实现),这反过来可能会导致你看到的错误。
我建议使用SharpArchitecture作为指导。
答案 1 :(得分:1)
我遇到了同样的问题,“构建nhibernate配置时,已经添加了一个具有相同密钥的项目。”
我发生的事情是,两个线程以编程方式构建不同的配置,旨在同时连接到不同的数据库。
我在整个配置制造商周围加了一个锁,但问题就消失了。
所以我猜配置对象取决于一些内部全局状态,即假设配置本身是一个单独的(因为我猜它会是,如果它完全由文件驱动,而不是以编程方式构建)。
答案 2 :(得分:1)
我意识到这是一个老问题,但几天前我使用NHibernate 3.0发生了类似的错误。
对于可能偶然发现此问题的读者:这是旧版本NHibernate中已知线程安全问题的结果。这已在3.2版中修复,但旧版本将没有修复,可能会产生此问题。此错误条目描述了问题:https://nhibernate.jira.com/browse/NH-3271
答案 3 :(得分:1)
我走了@joel truher所写的路线。但是,我想把代码放在这里。
public class NHibernateBootstrapper
{
private static readonly object _sessionFactoryLock = new object();
private static ISessionFactory _sessionFactory;
public static ISessionFactory CreateThreadStaticSessionFactory(string connectionString, bool exportSchema)
{
lock (_sessionFactoryLock)
{
if (_sessionFactory == null)
{
_sessionFactory = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008.ConnectionString(connectionString)
.AdoNetBatchSize(16))
.CurrentSessionContext<ThreadStaticSessionContext>()
.Mappings(m =>
{
m.FluentMappings.AddFromAssemblyOf<NHibernateBootstrapper>()
.Conventions.AddFromAssemblyOf<NHibernateBootstrapper>();
m.HbmMappings.AddFromAssemblyOf<NHibernateBootstrapper>();
})
.ExposeConfiguration(cfg => BuildSchema(cfg, exportSchema))
.BuildSessionFactory();
}
return _sessionFactory;
}
}
}
显然,您可以根据需要配置数据库。