Linq to NHibernate Sequence不包含任何元素



这听起来像是一个重复的问题,但我不这么认为。

这是我得到的错误,这听起来很常见。但我只是间歇性地得到这个错误。对于访问我的网站的用户数量,我估计这种情况只发生在大约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;

我知道repo有数据。尤其是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(重点是我的):

ISessionFactory是一个创建成本很高的线程安全对象,旨在由所有应用程序线程共享。会话是一种廉价的、非线程安全的对象,对于单个业务流程应该只使用一次,然后丢弃

所以你不应该像这样共享你的会话对象。如果可能,只在需要的时候创建一个,并尽快销毁它。

这里有一些很棒的深入阅读,讨论了上下文和生命周期,包括NHibernate。

问题很可能是使用单个会话,

public static ISession GetSession()
{
    return Instance.GetSession();
}

,因为你的web应用程序使用多个线程,每次这将返回相同的会话。根据定义,会话不是线程安全的。

最好的选择是配置DI为每个请求注入新的会话(如果您使用DI),例如对于Autofac

builder.Register(x => x.Resolve<ISessionFactory>().OpenSession())
    .InstancePerHttpRequest();

最新更新