让nihbernate会议永远开放?

时间:2011-05-16 00:45:04

标签: asp.net-mvc nhibernate quartz.net

我正在使用石英和nhibernate并遇到了问题。通常我会在完成Web请求时关闭所有的nhibernate会话,但是我有一个在应用程序启动时启动的调度程序,我需要传入一个我认为永远不应该关闭的nhibernate会话。

我不确定该怎么做。

Ninject

 public class NhibernateSessionFactoryProvider : Provider<ISessionFactory>
    {   
        protected override ISessionFactory CreateInstance(IContext context)
        {
            var sessionFactory = new NhibernateSessionFactory();
            return sessionFactory.GetSessionFactory();
        }
    }

  public class NhibernateModule : NinjectModule
    {
        public override void Load()
        {
            Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
            Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
        }
    }

Global.aspx

  protected void Application_Start()
    {
        // Hook our DI stuff when application starts
        IKernel kernel = SetupDependencyInjection();

        // get the reminder service HERE IS WHERE THE PROBLEMS START
        IScheduledRemindersService scheduledRemindersService = kernel.Get<IScheduledRemindersService>();

        scheduledRemindersService.StartTaskRemindersSchedule();

        RegisterMaps.Register();

        AreaRegistration.RegisterAllAreas();

        RegisterGlobalFilters(GlobalFilters.Filters);
        RegisterRoutes(RouteTable.Routes);


    }


    public IKernel SetupDependencyInjection()
    {
        IKernel kernel = CreateKernel();
        // Tell ASP.NET MVC 3 to use our Ninject DI Container
        DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));

        return kernel;
    }

    protected IKernel CreateKernel()
    {
        var modules = new INinjectModule[]
                          {
                             new NhibernateModule(),
                             new ServiceModule(),
                             new RepoModule()
                          };

        return new StandardKernel(modules);
    }

//导致我出现问题的服务。 Ninject将绑定reminderRepo并给它一个nihbernate会话。

private readonly IReminderRepo reminderRepo;
private readonly ISchedulerFactory schedulerFactory;

public ScheduledRemindersService(IReminderRepo reminderRepo)
{
    this.reminderRepo = reminderRepo;
    schedulerFactory = new StdSchedulerFactory();
}

public void StartTaskRemindersSchedule()
{

    IScheduler scheduler = schedulerFactory.GetScheduler();

    scheduler.Start();

    JobDetail jobDetail = new JobDetail("TaskRemindersJob",null,typeof(TaskReminderJob));
    jobDetail.JobDataMap["reminderRepo"] = reminderRepo;

    DateTime evenMinuteDate = TriggerUtils.GetEvenMinuteDate(DateTime.UtcNow);


    SimpleTrigger trigger = new SimpleTrigger("TaskRemindersTrigger", null,
                        DateTime.UtcNow,
                        null,
                        SimpleTrigger.RepeatIndefinitely,
                        TimeSpan.FromMinutes(1));

    scheduler.ScheduleJob(jobDetail, trigger);
}

所以我需要将提醒笔记传递给上面的工作

jobDetail.JobDataMap["reminderRepo"] = reminderRepo;

这是你将某些东西传递给工作的唯一方法。每次执行计划时都会重新创建一个作业,我假设它使用了我发送的相同提醒笔记本。

我的服务层代码永远不会再次执行,当然应用程序也会启动(除非我重新部署网站)

工作

 public class TaskReminderJob : IJob
    {


        public void Execute(JobExecutionContext context)
        {
            JobDataMap dataMap = context.JobDetail.JobDataMap;
            ReminderRepo reminderRepo = dataMap["reminderRepo"] as ReminderRepo;

            if (context.ScheduledFireTimeUtc.HasValue && context.NextFireTimeUtc.HasValue && reminderRepo != null)
            {
                DateTime start = context.ScheduledFireTimeUtc.Value;
                DateTime end = context.NextFireTimeUtc.Value;

                List<PersonalTaskReminder> personalTaskReminders = reminderRepo.GetPersonalTaskReminders(start, end);

                if (personalTaskReminders.Count > 0)
                {
                    reminderRepo.DeletePersonalTaskReminders(personalTaskReminders.Select(x => x.ReminderId).ToList());


                }

            }
        }

提醒回购。 (当这个repo被实例化时,应该给出一个会持续到请求结束的会话)

  public class ReminderRepo : IReminderRepo
    {

        private readonly ISession session;

        public ReminderRepo(ISession session)
        {
            this.session = session;
        }

        public List<PersonalTaskReminder> GetPersonalTaskReminders(DateTime start, DateTime end)
        {
            List<PersonalTaskReminder> personalTaskReminders = session.Query<PersonalTaskReminder>().Where(x => x.DateToBeSent <= start && x.DateToBeSent <= end).ToList();
            return personalTaskReminders;
        }

        public void DeletePersonalTaskReminders(List<int> reminderId)
        {
            const string query = "DELETE FROM PersonalTaskReminder WHERE ReminderId IN (:reminderId)";
            session.CreateQuery(query).SetParameterList("reminderId", reminderId).ExecuteUpdate();
        }


        public void Commit()
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                transaction.Commit();
            }
        }


    }

所以我需要一些方法让我的提醒保持会话活着。我所有其他回购的所有其他会议应该像我现在一样。只有这一个似乎需要永远活着。

修改

我每次尝试新会话,所以我传递了IsessionFactory。可能不是100%最好,但这是我能弄清楚如何获得一些新会议的唯一方法。

但是我不知道我的会话是否仍在通过ninject关闭,因为我现在手动传递会话。我现在在想,但无法验证。

 **private readonly ISessionFactory sessionFactory;**
private readonly ISchedulerFactory schedulerFactory;

public ScheduledRemindersService(ISessionFactory sessionFactory)
{
    **this.sessionFactory = sessionFactory;**
    schedulerFactory = new StdSchedulerFactory();
}

public void StartTaskRemindersSchedule()
{

    IScheduler scheduler = schedulerFactory.GetScheduler();

    scheduler.Start();

    JobDetail jobDetail = new JobDetail("TaskRemindersJob",null,typeof(TaskReminderJob));
    **jobDetail.JobDataMap["reminderRepo"] = sessionFactory;**

    DateTime evenMinuteDate = TriggerUtils.GetEvenMinuteDate(DateTime.UtcNow);


    SimpleTrigger trigger = new SimpleTrigger("TaskRemindersTrigger", null,
                        DateTime.UtcNow,
                        null,
                        SimpleTrigger.RepeatIndefinitely,
                        TimeSpan.FromMinutes(1));

    scheduler.ScheduleJob(jobDetail, trigger);
}

所以我的global.aspx是一样的但是因为ninject现在看到“ScheduledRemindersService”现在接受了一个nhibernate会话工厂,它为我绑定一个我可以使用的。

然后我把它传递给工作。

public void Execute(JobExecutionContext context)
{
    JobDataMap dataMap = context.JobDetail.JobDataMap;
    ISessionFactory sessionFactory = dataMap["reminderRepo"] as ISessionFactory;

    if (sessionFactory != null)
    {
        ISession openSession = sessionFactory.OpenSession();
        ReminderRepo reminderRepo = new ReminderRepo(openSession);
    }
}

然后我将它传递给我的ReminderRepo所以我猜它忽略了来自ninject的自动会话绑定但是我不是100%肯定因此我不确定我的会话是否被关闭。

3 个答案:

答案 0 :(得分:7)

是否有理由这项工作不能在每次运行时打开一个新会话?据推测,它是定期运行而不是永远运行。

永远打开东西通常是遇到奇怪行为的必然方式。

希望这有帮助。

答案 1 :(得分:1)

我认为解决这个问题的做法完全是错误的。 Web应用程序的应用程序范围不是安排事件或尝试保持“状态”的地方,因为它仍然是一个网站,并且Web服务器不会始终使应用程序“已启动”。这在服务器重新启动之后和应用程序请求之前,在应用程序池回收期间以及负载平衡等其他错误情况下发生。

安排某事的最佳位置是使用Windows服务或构建控制台应用程序,然后使用Windows调度程序定期运行它。如果依赖于应用程序状态,任何类似的轮询操作都会导致重大问题。

即使您可以依赖它,我也会非常担心让数据上下文无限期地存在的内存成本。

查看构建一个控制台应用程序 - 即使你以前没有这样做,你也会发现它很容易。

另一个好处是,当您确定自己在做什么时,可以相对轻松地将控制台应用程序切换到Windows服务。

答案 2 :(得分:0)

我最近开发了类似的解决方案 我选择使用“ Windows服务”来管理我的日程安排 无论如何,我不明白为什么你这样做:

ISessionFactory sessionFactory = dataMap["reminderRepo"] as ISessionFactory;

由于我遇到了同样的问题,我决定(仅在这种情况下)从StructureMap获取一个SessionFactory(这就是我所使用的)并自己打开一个新的会话。

这是我的工作:

public class ReminderScheduleJob : IStatefulJob
    {
        private readonly ILogger _Logger;
        private readonly ISessionFactory _SessionFactory;

        public ReminderScheduleJob()
        {
            this._Logger = ObjectFactory.GetInstance<ILogger>();
            this._SessionFactory = ObjectFactory.GetInstance<ISessionFactory>();
        }

        void IJob.Execute(JobExecutionContext context)
        {
            using (var session = this._SessionFactory.OpenSession())
            {
                using (var tx = session.BeginTransaction())
                {
                ...
                }
            }

        }
    }

<强>更新

这是我的nHibernate注册表(请参阅structureMap获取更多信息),这是在我的应用程序启动时调用的(asp.net或windows服务):

public class NhibernateRegistry: Registry
{
    public NhibernateRegistry()
    {

        For<ISessionFactory>()
            .Singleton()
            .Use(new BpReminders.Data.NH.NHibernateSessionFactory(myConnectionString, schemaOperation).SessionFactory);

        For<IUnitOfWork>()
            .HybridHttpOrThreadLocalScoped()
            .Use<BpReminders.Data.NH.UnitOfWork>();

        For<ISession>()
            .HybridHttpOrThreadLocalScoped()
            .Use(o => ((BpReminders.Data.NH.UnitOfWork)o.GetInstance<IUnitOfWork>()).CurrentSession);
    }
}

我使用了一个工作单元,但这对你没有任何影响 我已经为Quartz.net定义了另一个注册表(structureMap),因为我想要(这就是他们所说的)它是单身:

public class QuartzRegistry : Registry
{
    public QuartzRegistry()
    {
        var properties = new NameValueCollection();

        // I set all the properties here cause I persist my jobs etc on a DB.

        For<ISchedulerFactory>()
            .Singleton()
            .Use(new StdSchedulerFactory(properties));

        For<IScheduler>()
            .Singleton()
            .Use(x => x.GetInstance<ISchedulerFactory>().GetScheduler());
    }
}

现在,在global.asax中,我将启动调度程序,要求StructureMap / ninject解析IScheduler:

var scheduler = StructureMap.ObjectFactory.GetInstance<IScheduler>();
scheduler.Start();

我看到你在ScheduledRemindersService中创建了一个新的调度程序:

public ScheduledRemindersService(IReminderRepo reminderRepo)
{
    this.reminderRepo = reminderRepo;
    schedulerFactory = new StdSchedulerFactory();
}

因为在我的示例中,调度程序已经创建并以单例形式启动,您可以让ninject获取实例...并安排您的工作。
在你的工作(TaskReminderJob)中,你可以要求ObjectFactory解析ISessionFactory(参见我的类ReminderScheduleJob的构造)。 您现在可以打开一个新会话并将会话传递到您的存储库 一切都完成后,你可以处理你的会话。 希望我已经足够清楚了 正如我告诉你的那样,我有2层。我的网络应用程序负责安排作业,我有一个自定义的Windows服务(我没有使用Quartz.net提供的服务),它负责获取触发的事件并执行一些操作。