/// <summary>
        /// Initializes a new instance of the PersistedJobRun class.
        /// </summary>
        /// <param name="run">The <see cref="JobRun"/> being persisted.</param>
        public PersistedJobRun(JobRun run)
        {
            if (run == null)
            {
                throw new ArgumentNullException("run", "run cannot be null.");
            }

            this.ExecutionException = run.ExecutionException;
            this.FinishDate = run.FinishDate;
            this.JobId = run.JobId;
            this.JobType = run.Job != null ? run.Job.GetType().AssemblyQualifiedName : null;
            this.JobXml = run.Job != null ? run.Job.Serialize() : null;
            this.ScheduleName = run.ScheduleName;
            this.StartDate = run.StartDate;
        }
        /// <summary>
        /// Performs the concrete finishing of the given job run.
        /// </summary>
        /// <param name="run">A job run to finish.</param>
        /// <param name="record">The run's related record.</param>
        /// <param name="trans">The transaction to access the job store in.</param>
        private void FinishJobRun(JobRun run, JobRecord record, IJobStoreTransaction trans)
        {
            record.FinishDate = run.FinishDate;

            if (run.ExecutionException != null)
            {
                record.Exception = new ExceptionXElement(run.ExecutionException).ToString();
                record.Status = JobStatus.Failed;

                this.RaiseEvent(this.Error, new JobErrorEventArgs(record, run.ExecutionException));
                this.EnqueueJobForRetry(run.Job, trans);
            }
            else if (run.WasRecovered)
            {
                record.Status = JobStatus.Interrupted;
            }
            else
            {
                record.Status = JobStatus.Succeeded;
            }

            if (this.DeleteRecordsOnSuccess)
            {
                this.store.DeleteJob(record.Id.Value, trans);
            }
            else
            {
                this.store.SaveJob(record, trans);
            }

            this.runs.Remove(record.Id.Value);
            this.RaiseEvent(this.FinishJob, new JobRecordEventArgs(record));
        }
        /// <summary>
        /// Executes any scheduled jobs that are due.
        /// </summary>
        private void ExecuteScheduledJobs()
        {
            int count = this.MaximumConcurrency - this.ExecutingJobCount;

            if (count > 0)
            {
                DateTime now = DateTime.UtcNow;
                long heartbeat = this.lastScheduleCheck == null ? this.Heartbeat : (long)Math.Ceiling(now.Subtract(this.lastScheduleCheck.Value).TotalMilliseconds);
                this.lastScheduleCheck = now;

                var scheduleNames = this.Schedules.Select(s => s.Name);
                var tuples = ScheduledJobTuple.GetExecutableTuples(this.ScheduledJobs, now, heartbeat, count);

                using (IJobStoreTransaction trans = this.store.BeginTransaction())
                {
                    try
                    {
                        foreach (ScheduledJobTuple tuple in tuples)
                        {
                            JobRecord record = ScheduledJob.CreateRecord(tuple.Schedule, tuple.ScheduledJob, now);

                            bool running = this.runs.GetAll().Any(
                                r => tuple.Schedule.Name.Equals(r.ScheduleName, StringComparison.OrdinalIgnoreCase) &&
                                     tuple.ScheduledJob.JobType.StartsWith(record.JobType, StringComparison.OrdinalIgnoreCase));

                            if (!running)
                            {
                                IJob job = null;
                                Exception toJobEx = null;

                                try
                                {
                                    job = ScheduledJob.CreateFromConfiguration(tuple.ScheduledJob);
                                }
                                catch (ConfigurationErrorsException ex)
                                {
                                    toJobEx = ex;
                                    this.RaiseEvent(this.Error, new JobErrorEventArgs(record, toJobEx));
                                }

                                if (job != null)
                                {
                                    record.Name = job.Name;
                                    record.JobType = JobRecord.JobTypeString(job);
                                    record.Data = job.Serialize();
                                    this.store.SaveJob(record, trans);

                                    JobRun run = new JobRun(record.Id.Value, job);
                                    run.Finished += new EventHandler<JobRunEventArgs>(this.JobRunFinished);
                                    this.runs.Add(run);

                                    run.Start();
                                    this.RaiseEvent(this.ExecuteScheduledJob, new JobRecordEventArgs(record));
                                }
                                else
                                {
                                    record.Status = JobStatus.FailedToLoadType;
                                    record.FinishDate = now;
                                    record.Exception = new ExceptionXElement(toJobEx).ToString();
                                }

                                this.store.SaveJob(record, trans);
                            }
                        }

                        this.runs.Flush();
                        trans.Commit();
                    }
                    catch
                    {
                        trans.Rollback();
                        throw;
                    }
                }
            }
        }
        /// <summary>
        /// Dequeues pending jobs in the job store.
        /// </summary>
        private void DequeueJobs()
        {
            int count = this.MaximumConcurrency - this.ExecutingJobCount;

            if (count > 0)
            {
                DateTime now = DateTime.UtcNow;

                using (IJobStoreTransaction trans = this.store.BeginTransaction())
                {
                    try
                    {
                        foreach (var record in this.store.GetJobs(JobStatus.Queued, count, now, trans))
                        {
                            record.Status = JobStatus.Started;
                            record.StartDate = now;

                            IJob job = null;
                            Exception toJobEx = null;

                            try
                            {
                                job = record.ToJob();
                            }
                            catch (InvalidOperationException ex)
                            {
                                toJobEx = ex.InnerException ?? ex;
                                this.RaiseEvent(this.Error, new JobErrorEventArgs(record, toJobEx));
                            }

                            if (job != null)
                            {
                                JobRun run = new JobRun(record.Id.Value, job);
                                run.Finished += new EventHandler<JobRunEventArgs>(this.JobRunFinished);
                                this.runs.Add(run);

                                run.Start();
                            }
                            else
                            {
                                record.Status = JobStatus.FailedToLoadType;
                                record.FinishDate = now;
                                record.Exception = new ExceptionXElement(toJobEx).ToString();
                            }

                            this.store.SaveJob(record, trans);
                            this.RaiseEvent(this.DequeueJob, new JobRecordEventArgs(record));
                        }

                        this.runs.Flush();
                        trans.Commit();
                    }
                    catch
                    {
                        trans.Rollback();
                        throw;
                    }
                }
            }
        }
 /// <summary>
 /// Adds a job run to this instance.
 /// </summary>
 /// <param name="jobRun">The job run to add.</param>
 public void Add(JobRun jobRun)
 {
     lock (this.runs)
     {
         this.runs.Add(jobRun);
     }
 }