间歇性错误 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();