private JobDto CreateJobInState(HangfireDbContext database, int jobId, string stateName)
        {
            var job = Job.FromExpression(() => SampleMethod("wrong"));

            var jobState = new StateDto
            {
                CreatedAt = database.GetServerTimeUtc(),
                Data = stateName == EnqueuedState.StateName
                           ? string.Format(" {{ 'EnqueuedAt': '{0}' }}", database.GetServerTimeUtc().ToString("o"))
                           : "{}",
                JobId = jobId
            };
            database.State.InsertOne(jobState);

            var jobDto = new JobDto
            {
                Id = jobId,
                InvocationData = JobHelper.ToJson(InvocationData.Serialize(job)),
                Arguments = "['Arguments']",
                StateName = stateName,
                CreatedAt = database.GetServerTimeUtc(),
                StateId = jobState.Id
            };
            database.Job.InsertOne(jobDto);

            var jobQueueDto = new JobQueueDto
            {
                FetchedAt = null,
                Id = jobId * 10,
                JobId = jobId,
                Queue = DefaultQueue
            };

            if (stateName == FetchedStateName)
            {
                jobQueueDto.FetchedAt = database.GetServerTimeUtc();
            }

            database.JobQueue.InsertOne(jobQueueDto);

            return jobDto;
        }
		public MongoDistributedLock(string resource, TimeSpan timeout, HangfireDbContext database, MongoStorageOptions options)
		{
			if (String.IsNullOrEmpty(resource) == true)
				throw new ArgumentNullException("resource");

			if (database == null)
				throw new ArgumentNullException("database");

			if (options == null)
				throw new ArgumentNullException("options");

			_resource = resource;
			_database = database;
			_options = options;

			try
			{
				// Remove dead locks
				database.DistributedLock.Remove(Query.And(Query<DistributedLockDto>.EQ(_ => _.Resource, resource),
					Query<DistributedLockDto>.LT(_ => _.Heartbeat, database.GetServerTimeUtc().Subtract(options.DistributedLockLifetime))));

				// Check lock
				DateTime lockTimeoutTime = DateTime.Now.Add(timeout);
				bool isLockedBySomeoneElse;
				bool isFirstAttempt = true;
				do
				{
					isLockedBySomeoneElse = database
						.DistributedLock
						.FindOne(Query.And(Query<DistributedLockDto>.EQ(_ => _.Resource, resource),
							Query<DistributedLockDto>.NE(_ => _.ClientId, _options.ClientId))) != null;

					if (isFirstAttempt == true)
						isFirstAttempt = false;
					else
						Thread.Sleep((int)timeout.TotalMilliseconds / 10);
				}
				while ((isLockedBySomeoneElse == true) && (lockTimeoutTime >= DateTime.Now));

				// Set lock
				if (isLockedBySomeoneElse == false)
				{
					database.DistributedLock.FindAndModify(new FindAndModifyArgs
					{
						Query = Query<DistributedLockDto>.EQ(_ => _.Resource, resource),
						Update = Update.Combine(
							Update<DistributedLockDto>.Set(_ => _.ClientId, _options.ClientId),
							Update<DistributedLockDto>.Inc(_ => _.LockCount, 1),
							Update<DistributedLockDto>.Set(_ => _.Heartbeat, database.GetServerTimeUtc())
							),
						Upsert = true
					});

					StartHeartBeat();
				}
				else
				{
					throw new MongoDistributedLockException(String.Format("Could not place a lock on the resource '{0}': {1}.", _resource, "The lock request timed out"));
				}
			}
			catch (Exception ex)
			{
				if (ex is MongoDistributedLockException)
					throw;
				else
					throw new MongoDistributedLockException(String.Format("Could not place a lock on the resource '{0}': {1}.", _resource, "Check inner exception for details"), ex);
			}
		}
		private static int CreateJobQueueRecord(HangfireDbContext connection, string jobId, string queue)
		{
			var jobQueue = new JobQueueDto
			{
				JobId = int.Parse(jobId),
				Queue = queue,
				FetchedAt = connection.GetServerTimeUtc()
			};

			connection.JobQueue.Insert(jobQueue);

			return jobQueue.Id;
		}
        private static JobQueueDto CreateJobQueueDto(HangfireDbContext connection, string queue, bool isFetched)
        {
            var state = new StateDto();
            AsyncHelper.RunSync(() => connection.State.InsertOneAsync(state));

            var job = new JobDto
            {
                CreatedAt = connection.GetServerTimeUtc(),
                StateId = state.Id
            };

            AsyncHelper.RunSync(() => connection.Job.InsertOneAsync(job));

            var jobQueue = new JobQueueDto
            {
                Queue = queue,
                JobId = job.Id
            };

            if (isFetched)
            {
                jobQueue.FetchedAt = connection.GetServerTimeUtc().AddDays(-1);
            }

            AsyncHelper.RunSync(() => connection.JobQueue.InsertOneAsync(jobQueue));

            return jobQueue;
        }
        /// <summary>
        /// Creates MongoDB distributed lock
        /// </summary>
        /// <param name="resource">Lock resource</param>
        /// <param name="timeout">Lock timeout</param>
        /// <param name="database">Lock database</param>
        /// <param name="options">Database options</param>
        public MongoDistributedLock(string resource, TimeSpan timeout, HangfireDbContext database, MongoStorageOptions options)
        {
            if (String.IsNullOrEmpty(resource) == true)
                throw new ArgumentNullException("resource");

            if (database == null)
                throw new ArgumentNullException("database");

            if (options == null)
                throw new ArgumentNullException("options");

            _resource = resource;
            _database = database;
            _options = options;

            try
            {
                // Remove dead locks
                database.DistributedLock.DeleteManyAsync(
                    Builders<DistributedLockDto>.Filter.Eq(_ => _.Resource, resource) &
                    Builders<DistributedLockDto>.Filter.Lt(_ => _.Heartbeat, database.GetServerTimeUtc().Subtract(options.DistributedLockLifetime)));

                // Check lock
                DateTime lockTimeoutTime = DateTime.Now.Add(timeout);
                bool isLockedBySomeoneElse;
                bool isFirstAttempt = true;
                do
                {
                    isLockedBySomeoneElse = AsyncHelper.RunSync(() =>
                        database.DistributedLock
                            .Find(Builders<DistributedLockDto>.Filter.Eq(_ => _.Resource, resource) &
                                  Builders<DistributedLockDto>.Filter.Ne(_ => _.ClientId, _options.ClientId))
                            .FirstOrDefaultAsync()) != null;

                    if (isFirstAttempt == true)
                        isFirstAttempt = false;
                    else
                        Thread.Sleep((int)timeout.TotalMilliseconds / 10);
                }
                while ((isLockedBySomeoneElse == true) && (lockTimeoutTime >= DateTime.Now));

                // Set lock
                if (isLockedBySomeoneElse == false)
                {
                    AsyncHelper.RunSync(() => database.DistributedLock.FindOneAndUpdateAsync(
                        Builders<DistributedLockDto>.Filter.Eq(_ => _.Resource, resource),
                        Builders<DistributedLockDto>.Update.Combine(
                            Builders<DistributedLockDto>.Update.Set(_ => _.ClientId, _options.ClientId),
                            Builders<DistributedLockDto>.Update.Inc(_ => _.LockCount, 1),
                            Builders<DistributedLockDto>.Update.Set(_ => _.Heartbeat, database.GetServerTimeUtc())
                        ),
                        new FindOneAndUpdateOptions<DistributedLockDto> { IsUpsert = true }));

                    StartHeartBeat();
                }
                else
                {
                    throw new MongoDistributedLockException(String.Format("Could not place a lock on the resource '{0}': {1}.", _resource, "The lock request timed out"));
                }
            }
            catch (Exception ex)
            {
                if (ex is MongoDistributedLockException)
                    throw;
                else
                    throw new MongoDistributedLockException(String.Format("Could not place a lock on the resource '{0}': {1}.", _resource, "Check inner exception for details"), ex);
            }
        }