间歇性错误 Linq to NHibernate Sequence Contains no Elements
Intermittent error Linq to NHibernate Sequence Contains no Elements
这听起来像是一个重复的问题,但我不相信它是。
这是我遇到的错误,听起来很常见。但我只是间歇性地收到这个错误。对于访问我网站的用户数量,我估计这种情况只会发生大约 5%。
代码如下:
private TopWrestlers FillTopWrestlers()
{
try
{
var mostRecent = _wrestlerRankingRepo.Query().OrderByDescending(wr => wr.Season).First().Season;
var currentSeason = _configService.GetCurrentSeason();
if (mostRecent > currentSeason)
return null;
var tws =
_wrestlerRankingRepo.GetRankedWrestlers(currentSeason)
.Where(wr => wr.Rank <= 3)
.OrderBy(wr => wr.Rank)
.Select(wr => wr.Wrestler);
return new TopWrestlers
{
_125 = tws.FirstOrDefault(w => w.Roster.WeightClass == 125),
_133 = tws.FirstOrDefault(w => w.Roster.WeightClass == 133),
_141 = tws.FirstOrDefault(w => w.Roster.WeightClass == 141),
_149 = tws.FirstOrDefault(w => w.Roster.WeightClass == 149),
_157 = tws.FirstOrDefault(w => w.Roster.WeightClass == 157),
_165 = tws.FirstOrDefault(w => w.Roster.WeightClass == 165),
_174 = tws.FirstOrDefault(w => w.Roster.WeightClass == 174),
_184 = tws.FirstOrDefault(w => w.Roster.WeightClass == 184),
_197 = tws.FirstOrDefault(w => w.Roster.WeightClass == 197),
_285 = tws.FirstOrDefault(w => w.Roster.WeightClass == 285)
};
}
catch (Exception ex)
{
_errorService.LogError(new Exception("Exception occurred trying to retrieve the top wrestlers for each weight on the home page. " + ex.Message, ex));
return null;
}
}
我在第一行收到错误:
_wrestlerRankingRepo.Query().OrderByDescending(wr => wr.Season).First().Season;
我知道回购有数据。特别是因为它 returns 95% 的时间都很好。
任何关于如何解决这个错误的帮助?我什至无法重新创建问题。这是在网站的主页上。所以我不认为这与启动新会话有关....我在这里不知所措。
这是基本回购代码:
public abstract class BaseRepository<TRecord, TMap> : IBaseRepository<TRecord>
where TRecord : class, IEntity
where TMap : ClassMap<TRecord>, new()
{
private static Member _primaryKeyMember;
protected ISession Session;
protected IUserIdentity UserIdentity;
public BaseRepository(ISession session)
{
Session = session;
}
public BaseRepository(ISession session, IUserIdentity userIdentity)
{
Session = session;
UserIdentity = userIdentity;
}
public void Delete(TRecord obj)
{
Session.Delete(obj);
Session.Flush();
}
public void Save(TRecord value)
{
Session.SaveOrUpdate(value);
Session.Flush();
}
public void Save(IEnumerable<TRecord> values)
{
using (ITransaction sessionTransaction = Session.BeginTransaction())
{
try
{
foreach (TRecord value in values)
Session.SaveOrUpdate(value);
sessionTransaction.Commit();
Session.Flush();
}
catch
{
sessionTransaction.Rollback();
throw;
}
}
}
public virtual IQueryable<TRecord> Query()
{
return Session.Query<TRecord>();
}
}
public interface IBaseRepository<TRecord>
where TRecord : IEntity
{
void Delete(TRecord obj);
void Save(TRecord value);
void Save(IEnumerable<TRecord> values);
IQueryable<TRecord> Query();
}
这是 WrestlerRankingRepo 代码:
public class WrestlerRankingRepository : BaseRepository<WrestlerRanking, WrestlerRankingMap>, IWrestlerRankingRepository
{
public WrestlerRankingRepository(ISession session) : base(session)
{
}
public IQueryable<WrestlerRanking> GetRankedWrestlers(int season)
{
return base.Query()
.Where(wr => wr.Rank != null)
.Where(wr => wr.Season == season)
.Where(wr => wr.IsCurrent)
.Where(wr => !wr.Wrestler.LastName.StartsWith("("));
}
public IQueryable<WrestlerRanking> GetRankedWrestlers(int season, int weight)
{
return GetRankedWrestlers(season)
.Where(wr => wr.WeightClass == weight);
}
public IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season)
{
return GetRankedWrestlers(season);
// for some reason it hates this: .Where(w => w.CurrentStats != null)
}
public IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season, int weight)
{
return GetRankedWrestlers(season)
.Where(w => w.WeightClass == weight);
// for some reason it hates this: .Where(w => w.CurrentStats != null)
}
}
public interface IWrestlerRankingRepository : IBaseRepository<WrestlerRanking>
{
IQueryable<WrestlerRanking> GetRankedWrestlers(int season);
IQueryable<WrestlerRanking> GetRankedWrestlers(int season, int weight);
IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season);
IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season, int weight);
}
SessionFactory 代码:
public static class SessionFactory
{
private static ISessionProvider _instance;
public static ISessionProvider Instance
{
get
{
if (_instance == null)
{
string sessionClass = ConfigurationManager.AppSettings["SessionProvider"];
if (string.IsNullOrWhiteSpace(sessionClass))
throw new ConfigurationErrorsException("Session Provider must be specified in the app.config");
_instance = (ISessionProvider)Activator.CreateInstance(Type.GetType(sessionClass));
}
return _instance;
}
}
[Obsolete]
public static ISession GetCurrentSession()
{
return GetSession();
}
public static ISession GetSession()
{
return Instance.GetSession();
}
public static string GetConnectionString()
{
return ConfigurationManager.ConnectionStrings["WrestleStat"].ConnectionString;
}
public static IPersistenceConfigurer BuildConfig()
{
return MsSqlConfiguration.MsSql2008.ConnectionString(GetConnectionString());
}
public static void BuildMappings(MappingConfiguration mappings)
{
mappings.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly());
}
}
public class SqlStatementInterceptor : EmptyInterceptor
{
public override SqlString OnPrepareStatement(SqlString sql)
{
Trace.WriteLine(sql.ToString());
return sql;
}
}
public interface ISessionProvider
{
ISession GetSession();
ISession OpenSession();
void CloseSession();
}
这是 Ninject 实现:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind(scanner => scanner
.From(new List<Assembly>
{
typeof (HomeController).Assembly,
typeof (Match).Assembly
})
.SelectAllClasses()
.BindAllInterfaces()
.Configure(b => b.InTransientScope())
);
kernel.Rebind<ISession>().ToMethod(icontext => SessionFactory.GetSession()).InRequestScope();
//kernel.Rebind<IUserIdentity>().ToMethod(i => MvcApplication.GetWebIdentity()).InRequestScope();
}
另一个相关的地方...
public class CoreWebSessionModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += OpenSession;
context.EndRequest += CloseSession;
}
private static void OpenSession(object sender, EventArgs e)
{
SessionFactory.Instance.OpenSession();
}
private static void CloseSession(object sender, EventArgs e)
{
SessionFactory.Instance.CloseSession();
}
public void Dispose()
{
}
}
还有一个
public class CoreWebSessionProvider : ISessionProvider
{
private static ISessionFactory _holder;
private static ISessionFactory MySessionFactory
{
get
{
if (_holder == null)
{
_holder = GetFluentConfiguration().BuildSessionFactory();
}
return _holder;
}
}
public ISession GetSession()
{
return HttpContext.Current != null ? MySessionFactory.GetCurrentSession() : null;
}
public ISession OpenSession()
{
var session = MySessionFactory.OpenSession();
ManagedWebSessionContext.Bind(HttpContext.Current, session);
return session;
}
public void CloseSession()
{
var session = ManagedWebSessionContext.Unbind(HttpContext.Current, MySessionFactory);
if (session != null)
{
if (session.Transaction != null && session.Transaction.IsActive)
session.Transaction.Rollback();
//else
// session.Flush();
if (session.IsOpen)
session.Close();
}
}
private static FluentConfiguration GetFluentConfiguration()
{
return
Fluently.Configure()
.Database(SessionFactory.BuildConfig())
.ExposeConfiguration(BuildSchema)
.Mappings(SessionFactory.BuildMappings);
}
public static Configuration GetConfiguration()
{
return GetFluentConfiguration().BuildConfiguration();
}
private static void BuildSchema(Configuration config)
{
config.SetProperty("current_session_context_class", "managed_web");
}
public IStatelessSession GetStatelessSession()
{
return MySessionFactory.OpenStatelessSession();
}
}
来自 NHibernate documentation
(强调我的):
An ISessionFactory is an expensive-to-create, threadsafe object intended to be shared by all application threads. An ISession is an inexpensive, non-threadsafe object that should be used once, for a single business process, and then discarded
所以你不应该像这样分享你的会话对象。如果可能,只在需要时创建一个并尽快销毁它。
一些关于上下文和生命周期的优秀 further reading here,包括 NHibernate。
问题很可能是使用单个会话,
public static ISession GetSession()
{
return Instance.GetSession();
}
并且由于您的 Web 应用程序使用多个线程,因此每次都会 return 相同的会话。根据定义,会话不是线程安全的。
最好的选择是将您的 DI 配置为在每个请求中注入新会话(如果您正在使用 DI),例如 Autofac
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession())
.InstancePerHttpRequest();
这听起来像是一个重复的问题,但我不相信它是。
这是我遇到的错误,听起来很常见。但我只是间歇性地收到这个错误。对于访问我网站的用户数量,我估计这种情况只会发生大约 5%。
代码如下:
private TopWrestlers FillTopWrestlers()
{
try
{
var mostRecent = _wrestlerRankingRepo.Query().OrderByDescending(wr => wr.Season).First().Season;
var currentSeason = _configService.GetCurrentSeason();
if (mostRecent > currentSeason)
return null;
var tws =
_wrestlerRankingRepo.GetRankedWrestlers(currentSeason)
.Where(wr => wr.Rank <= 3)
.OrderBy(wr => wr.Rank)
.Select(wr => wr.Wrestler);
return new TopWrestlers
{
_125 = tws.FirstOrDefault(w => w.Roster.WeightClass == 125),
_133 = tws.FirstOrDefault(w => w.Roster.WeightClass == 133),
_141 = tws.FirstOrDefault(w => w.Roster.WeightClass == 141),
_149 = tws.FirstOrDefault(w => w.Roster.WeightClass == 149),
_157 = tws.FirstOrDefault(w => w.Roster.WeightClass == 157),
_165 = tws.FirstOrDefault(w => w.Roster.WeightClass == 165),
_174 = tws.FirstOrDefault(w => w.Roster.WeightClass == 174),
_184 = tws.FirstOrDefault(w => w.Roster.WeightClass == 184),
_197 = tws.FirstOrDefault(w => w.Roster.WeightClass == 197),
_285 = tws.FirstOrDefault(w => w.Roster.WeightClass == 285)
};
}
catch (Exception ex)
{
_errorService.LogError(new Exception("Exception occurred trying to retrieve the top wrestlers for each weight on the home page. " + ex.Message, ex));
return null;
}
}
我在第一行收到错误:
_wrestlerRankingRepo.Query().OrderByDescending(wr => wr.Season).First().Season;
我知道回购有数据。特别是因为它 returns 95% 的时间都很好。
任何关于如何解决这个错误的帮助?我什至无法重新创建问题。这是在网站的主页上。所以我不认为这与启动新会话有关....我在这里不知所措。
这是基本回购代码:
public abstract class BaseRepository<TRecord, TMap> : IBaseRepository<TRecord>
where TRecord : class, IEntity
where TMap : ClassMap<TRecord>, new()
{
private static Member _primaryKeyMember;
protected ISession Session;
protected IUserIdentity UserIdentity;
public BaseRepository(ISession session)
{
Session = session;
}
public BaseRepository(ISession session, IUserIdentity userIdentity)
{
Session = session;
UserIdentity = userIdentity;
}
public void Delete(TRecord obj)
{
Session.Delete(obj);
Session.Flush();
}
public void Save(TRecord value)
{
Session.SaveOrUpdate(value);
Session.Flush();
}
public void Save(IEnumerable<TRecord> values)
{
using (ITransaction sessionTransaction = Session.BeginTransaction())
{
try
{
foreach (TRecord value in values)
Session.SaveOrUpdate(value);
sessionTransaction.Commit();
Session.Flush();
}
catch
{
sessionTransaction.Rollback();
throw;
}
}
}
public virtual IQueryable<TRecord> Query()
{
return Session.Query<TRecord>();
}
}
public interface IBaseRepository<TRecord>
where TRecord : IEntity
{
void Delete(TRecord obj);
void Save(TRecord value);
void Save(IEnumerable<TRecord> values);
IQueryable<TRecord> Query();
}
这是 WrestlerRankingRepo 代码:
public class WrestlerRankingRepository : BaseRepository<WrestlerRanking, WrestlerRankingMap>, IWrestlerRankingRepository
{
public WrestlerRankingRepository(ISession session) : base(session)
{
}
public IQueryable<WrestlerRanking> GetRankedWrestlers(int season)
{
return base.Query()
.Where(wr => wr.Rank != null)
.Where(wr => wr.Season == season)
.Where(wr => wr.IsCurrent)
.Where(wr => !wr.Wrestler.LastName.StartsWith("("));
}
public IQueryable<WrestlerRanking> GetRankedWrestlers(int season, int weight)
{
return GetRankedWrestlers(season)
.Where(wr => wr.WeightClass == weight);
}
public IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season)
{
return GetRankedWrestlers(season);
// for some reason it hates this: .Where(w => w.CurrentStats != null)
}
public IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season, int weight)
{
return GetRankedWrestlers(season)
.Where(w => w.WeightClass == weight);
// for some reason it hates this: .Where(w => w.CurrentStats != null)
}
}
public interface IWrestlerRankingRepository : IBaseRepository<WrestlerRanking>
{
IQueryable<WrestlerRanking> GetRankedWrestlers(int season);
IQueryable<WrestlerRanking> GetRankedWrestlers(int season, int weight);
IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season);
IQueryable<WrestlerRanking> GetRankedWrestlersWithMatches(int season, int weight);
}
SessionFactory 代码:
public static class SessionFactory
{
private static ISessionProvider _instance;
public static ISessionProvider Instance
{
get
{
if (_instance == null)
{
string sessionClass = ConfigurationManager.AppSettings["SessionProvider"];
if (string.IsNullOrWhiteSpace(sessionClass))
throw new ConfigurationErrorsException("Session Provider must be specified in the app.config");
_instance = (ISessionProvider)Activator.CreateInstance(Type.GetType(sessionClass));
}
return _instance;
}
}
[Obsolete]
public static ISession GetCurrentSession()
{
return GetSession();
}
public static ISession GetSession()
{
return Instance.GetSession();
}
public static string GetConnectionString()
{
return ConfigurationManager.ConnectionStrings["WrestleStat"].ConnectionString;
}
public static IPersistenceConfigurer BuildConfig()
{
return MsSqlConfiguration.MsSql2008.ConnectionString(GetConnectionString());
}
public static void BuildMappings(MappingConfiguration mappings)
{
mappings.FluentMappings.AddFromAssembly(Assembly.GetExecutingAssembly());
}
}
public class SqlStatementInterceptor : EmptyInterceptor
{
public override SqlString OnPrepareStatement(SqlString sql)
{
Trace.WriteLine(sql.ToString());
return sql;
}
}
public interface ISessionProvider
{
ISession GetSession();
ISession OpenSession();
void CloseSession();
}
这是 Ninject 实现:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind(scanner => scanner
.From(new List<Assembly>
{
typeof (HomeController).Assembly,
typeof (Match).Assembly
})
.SelectAllClasses()
.BindAllInterfaces()
.Configure(b => b.InTransientScope())
);
kernel.Rebind<ISession>().ToMethod(icontext => SessionFactory.GetSession()).InRequestScope();
//kernel.Rebind<IUserIdentity>().ToMethod(i => MvcApplication.GetWebIdentity()).InRequestScope();
}
另一个相关的地方...
public class CoreWebSessionModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += OpenSession;
context.EndRequest += CloseSession;
}
private static void OpenSession(object sender, EventArgs e)
{
SessionFactory.Instance.OpenSession();
}
private static void CloseSession(object sender, EventArgs e)
{
SessionFactory.Instance.CloseSession();
}
public void Dispose()
{
}
}
还有一个
public class CoreWebSessionProvider : ISessionProvider
{
private static ISessionFactory _holder;
private static ISessionFactory MySessionFactory
{
get
{
if (_holder == null)
{
_holder = GetFluentConfiguration().BuildSessionFactory();
}
return _holder;
}
}
public ISession GetSession()
{
return HttpContext.Current != null ? MySessionFactory.GetCurrentSession() : null;
}
public ISession OpenSession()
{
var session = MySessionFactory.OpenSession();
ManagedWebSessionContext.Bind(HttpContext.Current, session);
return session;
}
public void CloseSession()
{
var session = ManagedWebSessionContext.Unbind(HttpContext.Current, MySessionFactory);
if (session != null)
{
if (session.Transaction != null && session.Transaction.IsActive)
session.Transaction.Rollback();
//else
// session.Flush();
if (session.IsOpen)
session.Close();
}
}
private static FluentConfiguration GetFluentConfiguration()
{
return
Fluently.Configure()
.Database(SessionFactory.BuildConfig())
.ExposeConfiguration(BuildSchema)
.Mappings(SessionFactory.BuildMappings);
}
public static Configuration GetConfiguration()
{
return GetFluentConfiguration().BuildConfiguration();
}
private static void BuildSchema(Configuration config)
{
config.SetProperty("current_session_context_class", "managed_web");
}
public IStatelessSession GetStatelessSession()
{
return MySessionFactory.OpenStatelessSession();
}
}
来自 NHibernate documentation
(强调我的):
An ISessionFactory is an expensive-to-create, threadsafe object intended to be shared by all application threads. An ISession is an inexpensive, non-threadsafe object that should be used once, for a single business process, and then discarded
所以你不应该像这样分享你的会话对象。如果可能,只在需要时创建一个并尽快销毁它。
一些关于上下文和生命周期的优秀 further reading here,包括 NHibernate。
问题很可能是使用单个会话,
public static ISession GetSession()
{
return Instance.GetSession();
}
并且由于您的 Web 应用程序使用多个线程,因此每次都会 return 相同的会话。根据定义,会话不是线程安全的。
最好的选择是将您的 DI 配置为在每个请求中注入新会话(如果您正在使用 DI),例如 Autofac
builder.Register(x => x.Resolve<ISessionFactory>().OpenSession())
.InstancePerHttpRequest();