// Updates and Inserts to certain tables have been explicitly implemented here in order to reduce the overhead
		// of the LINQ to SQL dynamic query generation which can greatly impact overall performance of the data store.
		// These overrides have been implemented in accordance with the requirements defined at
		// http://msdn.microsoft.com/en-us/library/vstudio/bb546188%28v=vs.100%29.aspx
		// In particular, updates should throw a ChangeConflictException if the specific update version was not found,
		// and inserts should update the entity with the inserted row's identity. Both updates and inserts should
		// update the entity with the row's version.

		partial void InsertWorkItem(WorkItem instance)
		{
			const string commandText = "INSERT INTO [WorkItem]"
			                           + " ([DeleteTime], [ExpirationTime], [FailureCount], [Priority], [ProcessTime], [RequestedTime],"
			                           + "	[ScheduledTime], [SerializedProgress], [SerializedRequest], [Status], [StudyInstanceUid], [Type])"
			                           + " VALUES (@deleteTime, @expirationTime, @failureCount, @priority, @processTime, @requestedTime,"
			                           + "  @scheduledTime, @progress, @request, @status, @studyInstanceUid, @type)";
			using (var cmd = Connection.CreateCommand(commandText, Transaction))
			{
				cmd.SetParameter("deleteTime", instance.DeleteTime);
				cmd.SetParameter("expirationTime", instance.ExpirationTime);
				cmd.SetParameter("failureCount", instance.FailureCount);
				cmd.SetParameter("priority", (int) instance.Priority);
				cmd.SetParameter("processTime", instance.ProcessTime);
				cmd.SetParameter("requestedTime", instance.RequestedTime);
				cmd.SetParameter("scheduledTime", instance.ScheduledTime);
				cmd.SetParameter("progress", instance.SerializedProgress);
				cmd.SetParameter("request", instance.SerializedRequest);
				cmd.SetParameter("status", (int) instance.Status);
				cmd.SetParameter("studyInstanceUid", instance.StudyInstanceUid);
				cmd.SetParameter("type", instance.Type);

				cmd.ExecuteNonQuery();

				// this is an INSERT, so we must retrieve and update the row id and version
				cmd.ReadInsertedRowIdentity(instance, "WorkItem");
			}
		}
		public InsertWorkItemCommand(WorkItem item, string studyInstanceUid, string seriesInstanceUid, string sopInstanceUid)
			: base("Insert a WorkItem")
		{
			_request = item.Request;
			_studyInstanceUid = studyInstanceUid;
			ExpirationDelaySeconds = 60;

			WorkItem = item;

			WorkItemUid = new WorkItemUid
			              	{
			              		Complete = false,
			              		FailureCount = 0,
			              		WorkItemOid = WorkItem.Oid,
			              		SeriesInstanceUid = seriesInstanceUid,
			              		SopInstanceUid = sopInstanceUid,
			              		Failed = false
			              	};
		}
 public static WorkItemData FromWorkItem(WorkItem item)
 {
     return new WorkItemData
                {
                    Type = item.Type,
                    Status = item.Status,
                    Priority = item.Priority,
                    DeleteTime = item.DeleteTime,
                    ExpirationTime = item.ExpirationTime,
                    ScheduledTime = item.ScheduledTime,
                    RequestedTime = item.RequestedTime,
                    FailureCount = item.FailureCount,
                    Identifier = item.Oid,
                    ProcessTime = item.ProcessTime,
                    StudyInstanceUid = item.StudyInstanceUid,
                    Request = item.Request,
                    Progress = item.Progress
                };
 }
		partial void UpdateWorkItem(WorkItem instance)
		{
			const string commandText = "UPDATE [WorkItem]"
			                           + "	SET [DeleteTime] = @deleteTime,"
			                           + "	[ExpirationTime] = @expirationTime,"
			                           + "	[FailureCount] = @failureCount,"
			                           + "	[Priority] = @priority,"
			                           + "	[ProcessTime] = @processTime,"
			                           + "	[RequestedTime] = @requestedTime,"
			                           + "	[ScheduledTime] = @scheduledTime,"
			                           + "	[SerializedProgress] = @progress,"
			                           + "	[SerializedRequest] = @request,"
			                           + "	[Status] = @status,"
			                           + "	[StudyInstanceUid] = @studyInstanceUid,"
			                           + "	[Type] = @type"
			                           + "	WHERE [Oid] = @oid"
			                           + "  AND [Version] = @version";
			using (var cmd = Connection.CreateCommand(commandText, Transaction))
			{
				cmd.SetParameter("oid", instance.Oid);
				cmd.SetParameter("deleteTime", instance.DeleteTime);
				cmd.SetParameter("expirationTime", instance.ExpirationTime);
				cmd.SetParameter("failureCount", instance.FailureCount);
				cmd.SetParameter("priority", (int) instance.Priority);
				cmd.SetParameter("processTime", instance.ProcessTime);
				cmd.SetParameter("requestedTime", instance.RequestedTime);
				cmd.SetParameter("scheduledTime", instance.ScheduledTime);
				cmd.SetParameter("progress", instance.SerializedProgress);
				cmd.SetParameter("request", instance.SerializedRequest);
				cmd.SetParameter("status", (int) instance.Status);
				cmd.SetParameter("studyInstanceUid", instance.StudyInstanceUid);
				cmd.SetParameter("type", instance.Type);
				cmd.SetParameter("version", instance.Version);

				// if update doesn't affect any rows, then a change conflict has occurred and we must inform LINQ-to-SQL about it
				if (cmd.ExecuteNonQuery() == 0)
					throw new ChangeConflictException();

				// this is an UPDATE, so we must retrieve and update the row version
				cmd.ReadUpdatedRowVersion(instance, "WorkItem");
			}
		}
 partial void UpdateWorkItem(WorkItem instance);
 partial void DeleteWorkItem(WorkItem instance);
        private IEnumerable<WorkItem> GetStudyWorkItemsScheduledBeforeOrHigherPriority(WorkItem workItem, WorkItemStatusFilter statusFilter)
        {
            //Don't bother if the work item's not for a study.
            if (String.IsNullOrEmpty(workItem.StudyInstanceUid))
                return new List<WorkItem>();

            var broker = _context.GetWorkItemBroker();

            var workItems = broker.GetWorkItemsScheduledBeforeOrHigherPriority(workItem.ScheduledTime, workItem.Priority, statusFilter, workItem.StudyInstanceUid);

            //Shouldn't get the same one back, but do this just to be safe.
            return workItems.Where(w => w.Oid != workItem.Oid);
        }
 partial void InsertWorkItem(WorkItem instance);
        private bool CanStartStudyUpdateTrigger(WorkItem workItem, out string reason)
        {
            //A retrieve (trigger) waits for other retrieves already running, as well as other running imports/receives (study import).
            Predicate<WorkItem> canRunConcurrently = w => w.Request.ConcurrencyType == WorkItemConcurrency.NonExclusive;
            if (CompetingStudyWorkItem(workItem, canRunConcurrently, out reason))
                return false;

            return !CompetingExclusiveWorkItem(workItem, out reason);
        }
        private bool CanStart(WorkItem item, out string reason)
        {
            if (item.Request.ConcurrencyType == WorkItemConcurrency.NonExclusive)
                return CanStartNonExclusive(item, out reason);

            if (item.Request.ConcurrencyType == WorkItemConcurrency.StudyUpdateTrigger)
                return CanStartStudyUpdateTrigger(item, out reason);

            if (item.Request.ConcurrencyType == WorkItemConcurrency.StudyUpdate)
                return CanStartStudyUpdate(item, out reason);

            if (item.Request.ConcurrencyType == WorkItemConcurrency.StudyDelete)
                return CanStartStudyDelete(item, out reason);

            if (item.Request.ConcurrencyType == WorkItemConcurrency.StudyRead)
                return CanStartStudyRead(item, out reason);

            return CanStartExclusive(item, out reason);
        }
        private IEnumerable<WorkItem> GetPendingWorkItemsScheduledBeforeOrHigherPriority(WorkItem workItem)
        {
            var broker = _context.GetWorkItemBroker();
            var workItems = broker.GetWorkItemsScheduledBeforeOrHigherPriority(workItem.ScheduledTime,
                                                                                            workItem.Priority,
                                                                                            WorkItemStatusEnum.Pending, null);

            //The stuff now running is not Pending anymore.
            //Note that we don't do anything with the items we postponed because we only change the Scheduled Time
            //for items with a time window, and that change will get picked up on the next query for items to "start".
            return ExcludeNowRunningItems(workItems);
        }
		/// <summary>
		/// Delete WorkItemUid entity.
		/// </summary>
		/// <param name="entity"></param>
		public void Delete(WorkItem entity)
		{
			Context.WorkItems.DeleteOnSubmit(entity);
		}
        private bool CompetingStudyWorkItem(WorkItem workItem, Predicate<WorkItem> canRunConcurrently, out string reason)
        {
            reason = string.Empty;

            //Not a study work item, no need to check.
            if (String.IsNullOrEmpty(workItem.StudyInstanceUid))
                return false;

            //The input item is either Pending or Idle.
            if (workItem.Status == WorkItemStatusEnum.Pending)
            {
                //A pending work item (same study) scheduled before, or of higher priority, should stop the current item from starting.
                //However, the only reason the other item would still be pending is if it was waiting for something else,
                //so we'll start as long as the current item doesn't have a concurrency conflict with the other item.
                //Then we can safely make the most of the available processing time.
                var moreImportantWorkItems = GetPendingStudyWorkItemsScheduledBeforeOrHigherPriority(workItem);
                if (MustWaitForAny(moreImportantWorkItems.Where(w => !canRunConcurrently(w)), out reason))
                    return true;

                //Anything already running or idle (same study) must finish first, regardless of priority or schedule.
                var runningOrIdleWorkItems = GetRunningOrIdleStudyWorkItems(workItem);
                if (MustWaitForAny(runningOrIdleWorkItems.Where(w => !canRunConcurrently(w)), out reason))
                    return true;
            }
            else //Idle
            {
                //Should never really happen, but only an already running item (same study), or another idle item scheduled before or of higher priority, can stop the current one from starting.
                var moreImportantWorkItems = GetIdleStudyWorkItemsScheduledBeforeOrHigherPriority(workItem);
                if (MustWaitForAny(moreImportantWorkItems.Where(w => !canRunConcurrently(w)), out reason))
                    return true;

                var runningWorkItems = GetRunningStudyWorkItems(workItem);
                if (MustWaitForAny(runningWorkItems.Where(w => !canRunConcurrently(w)), out reason))
                    return true;
            }

            return false;
        }
        /// <summary>
        /// For anything not itself exclusive, check whether or not there is a competing exclusive work item.
        /// </summary>
        /// <param name="workItem"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        private bool CompetingExclusiveWorkItem(WorkItem workItem, out string reason)
        {
            if (workItem.Status == WorkItemStatusEnum.Pending)
            {
                //The item is Pending and is NOT itself Exclusive.
                var runningOrIdleExclusiveWorkItems = GetRunningOrIdleExclusiveWorkItems();
                if (MustWaitForAny(runningOrIdleExclusiveWorkItems, out reason))
                    return true;

                var moreImportantExclusiveWorkItems = GetPendingExclusiveWorkItemsScheduledBeforeOrHigherPriority(workItem);
                if (MustWaitForAny(moreImportantExclusiveWorkItems, out reason))
                    return true;
            }
            else
            {
                //The item is Idle and is NOT itself Exclusive.

                //Although it would never actually happen, because the logic in this class prevents it, the only thing that could
                //possibly stop the current (NOT exclusive) work item from starting is an exclusive work item that is already running,
                //or another idle (exclusive) item that is scheduled before it, or is of a higher priority.
                var runningExclusiveWorkItems = GetRunningExclusiveWorkItems();
                if (MustWaitForAny(runningExclusiveWorkItems, out reason))
                    return true;

                var moreImportantExclusiveWorkItems = GetIdleExclusiveWorkItemsScheduledBeforeOrHigherPriority(workItem);
                if (MustWaitForAny(moreImportantExclusiveWorkItems, out reason))
                    return true;
            }

            return false;
        }
        private bool CanStartExclusive(WorkItem workItem, out string reason)
        {
            //The input item is either Pending or Idle.
            if (workItem.Status == WorkItemStatusEnum.Pending)
            {
                //The current item is Exclusive and Pending.

                //A pending work item scheduled before, or of higher priority, should stop the current item from starting.
                var moreImportantWorkItems = GetPendingWorkItemsScheduledBeforeOrHigherPriority(workItem);
                if (MustWaitForAny(moreImportantWorkItems, out reason))
                    return false;

                //Anything already running or idle must finish first, regardless of priority or schedule.
                var runningOrIdleWorkItems = GetRunningOrIdleWorkItems();
                if (MustWaitForAny(runningOrIdleWorkItems, out reason))
                    return false;
            }
            else
            {
                //The current item is Exclusive and Idle.

                //Should never really happen, but only an already running item, or another idle item scheduled before or of higher priority, can stop the current one from starting.
                var moreImportantWorkItems = GetIdleWorkItemsScheduledBeforeOrHigherPriority(workItem);
                if (MustWaitForAny(moreImportantWorkItems, out reason))
                    return false;

                var runningWorkItems = GetRunningWorkItems();
                if (MustWaitForAny(runningWorkItems, out reason))
                    return false;
            }

            return true;
        }
        private bool CanStartStudyRead(WorkItem workItem, out string reason)
        {
            Predicate<WorkItem> canRunConcurrently = w => w.Request.ConcurrencyType == WorkItemConcurrency.StudyRead || w.Request.ConcurrencyType == WorkItemConcurrency.NonExclusive;
            if (CompetingStudyWorkItem(workItem, canRunConcurrently, out reason))
                return false;

            return !CompetingExclusiveWorkItem(workItem, out reason);
        }
        private bool CanStartStudyUpdate(WorkItem workItem, out string reason)
        {
            //An import can start running when a retrieve is already running.
            Predicate<WorkItem> canRunConcurrently = w => w.Request.ConcurrencyType == WorkItemConcurrency.StudyUpdateTrigger || w.Request.ConcurrencyType == WorkItemConcurrency.NonExclusive;
            if (CompetingStudyWorkItem(workItem, canRunConcurrently, out reason))
                return false;

            return !CompetingExclusiveWorkItem(workItem, out reason);
        }
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="item">The WorkItem to create a proxy for.</param>
 public WorkItemStatusProxy(WorkItem item)
 {
     Item = item;
     Progress = item.Progress;
     Request = item.Request;
 }
 private void CheckDeleteStudyCanceled(DataAccessContext context, WorkItem workItem)
 {
     // Force the study to be visible again if its a DeleteStudyRequest we're canceling
     if (workItem.Type.Equals(DeleteStudyRequest.WorkItemTypeString))
     {
         var studyBroker = context.GetStudyBroker();
         var study = studyBroker.GetStudy(workItem.StudyInstanceUid);
         if (study != null)
         {
             study.Deleted = false;
         }
     }
 }
		/// <summary>
		/// Insert a WorkItem
		/// </summary>
		/// <param name="entity"></param>
		public void AddWorkItem(WorkItem entity)
		{
			Context.WorkItems.InsertOnSubmit(entity);
		}
		protected override void OnExecute(CommandProcessor theProcessor)
		{
			var workItemBroker = DataAccessContext.GetWorkItemBroker();

			DateTime now = Platform.Time;

			if (WorkItem != null)
			{
				// Already have a committed WorkItem, just set the Oid
				WorkItemUid.WorkItemOid = WorkItem.Oid;

				WorkItem = workItemBroker.GetWorkItem(WorkItem.Oid);
				if (WorkItem.Status == WorkItemStatusEnum.Idle
				    || WorkItem.Status == WorkItemStatusEnum.Pending
				    || WorkItem.Status == WorkItemStatusEnum.InProgress)
				{
					WorkItem.ExpirationTime = now.AddSeconds(ExpirationDelaySeconds);

					var workItemUidBroker = DataAccessContext.GetWorkItemUidBroker();
					workItemUidBroker.AddWorkItemUid(WorkItemUid);
				}
				else
				{
					WorkItem = null;
				}
			}

			if (WorkItem == null)
			{
				var list = workItemBroker.GetWorkItems(_request.WorkItemType, WorkItemStatusFilter.Active, _studyInstanceUid);
				if (list != null)
				{
					foreach (var item in list)
					{
						if (item.Request.GetType() == _request.GetType())
						{
							var thisRequest = _request as DicomReceiveRequest;
							if (thisRequest != null)
							{
								var request = item.Request as DicomReceiveRequest;
								if (request != null)
								{
									if (request.SourceServerName.Equals(thisRequest.SourceServerName))
									{
										WorkItem = item;
										break;
									}
								}
							}
							else
							{
								WorkItem = item;
								break;
							}
						}
					}
				}

				if (WorkItem == null)
				{
					WorkItem = new WorkItem
					           	{
					           		ScheduledTime = now.AddSeconds(WorkItemServiceSettings.Default.InsertDelaySeconds),
					           		Request = _request,
					           		ProcessTime = now.AddSeconds(WorkItemServiceSettings.Default.InsertDelaySeconds),
					           		Priority = _request.Priority,
					           		Type = _request.WorkItemType,
					           		DeleteTime = now.AddHours(2),
					           		ExpirationTime = now.AddSeconds(ExpirationDelaySeconds),
					           		RequestedTime = now,
					           		StudyInstanceUid = _studyInstanceUid,
					           		Status = WorkItemStatusEnum.Pending,
					           		Progress = _progress
					           	};

					workItemBroker.AddWorkItem(WorkItem);
					WorkItemUid.WorkItem = WorkItem;

					var workItemUidBroker = DataAccessContext.GetWorkItemUidBroker();
					workItemUidBroker.AddWorkItemUid(WorkItemUid);
				}
				else
				{
					WorkItem.ExpirationTime = now.AddSeconds(ExpirationDelaySeconds);
					WorkItemUid.WorkItemOid = WorkItem.Oid;
					var workItemUidBroker = DataAccessContext.GetWorkItemUidBroker();
					workItemUidBroker.AddWorkItemUid(WorkItemUid);
				}
			}
		}
 private IEnumerable<WorkItem> GetIdleStudyWorkItemsScheduledBeforeOrHigherPriority(WorkItem workItem)
 {
     var workItems = GetStudyWorkItemsScheduledBeforeOrHigherPriority(workItem, WorkItemStatusEnum.Idle);
     return ExcludeNowRunningItems(workItems);
 }
        private bool CanStartNonExclusive(WorkItem workItem, out string reason)
        {
            if (false)
            {
                Predicate<WorkItem> canRunConcurrently = w => w.Request.ConcurrencyType != WorkItemConcurrency.Exclusive;
                if (CompetingStudyWorkItem(workItem, canRunConcurrently, out reason))
                {
                    //Can't really ever get here because even though a "non-exclusive" item could technically be a "study work item",
                    //the only thing it can't run concurrently with is an "exclusive" item, whether it's a "study work item" or not.
                    //So, the only thing to check is whether or not there is a competing exclusive item.
                    return false;
                }
            }

            return !CompetingExclusiveWorkItem(workItem, out reason);
        }
        public WorkItemInsertResponse Insert(WorkItemInsertRequest request)
        {
            // TODO (CR Jun 2012): The fact that there is special processing in here for particular types of work items
            // indicates there is something wrong with the design that may make adding custom work item types difficult.
            // Maybe the different "processors" need to perform the insert, or at least have some kind of method (rule)
            // for processing the insert?

            var response = new WorkItemInsertResponse();

            using (var context = new DataAccessContext(DataAccessContext.WorkItemMutex))
            {
                DateTime now = Platform.Time;
                var broker = context.GetWorkItemBroker();

                if (request.Request.WorkItemType.Equals(ReindexRequest.WorkItemTypeString))
                {
                    var list = broker.GetWorkItems(request.Request.WorkItemType, null, null);
                    foreach (var workItem in list)
                    {
                        if (workItem.Status == WorkItemStatusEnum.Pending
                            || workItem.Status == WorkItemStatusEnum.InProgress)
                        {
                            response.Item = WorkItemDataHelper.FromWorkItem(workItem);
                            return response;
                        }
                    }
                }

                var deleteStudyRequest = request.Request as DeleteStudyRequest;
                if (deleteStudyRequest != null)
                {
                    var list = broker.GetWorkItems(request.Request.WorkItemType, null, deleteStudyRequest.Study.StudyInstanceUid);
                    foreach (var workItem in list)
                    {
                        if (workItem.Status == WorkItemStatusEnum.Pending
                            || workItem.Status == WorkItemStatusEnum.InProgress)
                        {
                            // Mark studies to delete as "deleted" in the database.
                            var studyBroker = context.GetStudyBroker();
                            var study = studyBroker.GetStudy(deleteStudyRequest.Study.StudyInstanceUid);
                            if (study != null)
                            {
                                study.Deleted = true;
                                context.Commit();
                            }

                            response.Item = WorkItemDataHelper.FromWorkItem(workItem);
                            return response;
                        }
                    }
                }

                var item = new WorkItem
                               {
                                   Request = request.Request,
                                   Progress = request.Progress,
                                   Type = request.Request.WorkItemType,
                                   Priority = request.Request.Priority,
                                   ScheduledTime = now.AddSeconds(WorkItemServiceSettings.Default.InsertDelaySeconds),
                                   ProcessTime = now.AddSeconds(WorkItemServiceSettings.Default.InsertDelaySeconds),
                                   DeleteTime = now.AddMinutes(WorkItemServiceSettings.Default.DeleteDelayMinutes),
                                   ExpirationTime = now.AddSeconds(WorkItemServiceSettings.Default.ExpireDelaySeconds),
                                   RequestedTime = now,
                                   Status = WorkItemStatusEnum.Pending
                               };

                var studyRequest = request.Request as WorkItemStudyRequest;
                if (studyRequest != null)
                {
                    item.StudyInstanceUid = studyRequest.Study.StudyInstanceUid;

                    if (request.Request.WorkItemType.Equals(DeleteStudyRequest.WorkItemTypeString))
                    {
                        // Mark studies to delete as "deleted" in the database.
                        var studyBroker = context.GetStudyBroker();
                        var study = studyBroker.GetStudy(studyRequest.Study.StudyInstanceUid);
                        if (study != null)
                            study.Deleted = true;
                    }
                }

                broker.AddWorkItem(item);
                
                context.Commit();

                response.Item = WorkItemDataHelper.FromWorkItem(item);
            }

            // Cache the UserIdentityContext for later use by the shred
            if (request.Request.WorkItemType.Equals(ImportFilesRequest.WorkItemTypeString))
                UserIdentityCache.Put(response.Item.Identifier,UserIdentityContext.CreateFromCurrentThreadPrincipal());

			WorkItemPublishSubscribeHelper.PublishWorkItemChanged(WorkItemsChangedEventType.Update, response.Item);
            if (WorkItemProcessor.Instance != null)
                WorkItemProcessor.Instance.SignalThread();

            return response;
        }
 private IEnumerable<WorkItem> GetRunningOrIdleStudyWorkItems(WorkItem workItem)
 {
     var workItems = GetStudyWorkItems(workItem, WorkItemStatusFilter.RunningOrIdle);
     //Include anything we just started for the same study. Any that remained Idle will also be in the list.
     var nowRunningSameStudy = _nowRunningWorkItems.Where(w => w.StudyInstanceUid == workItem.StudyInstanceUid);
     return CombineWorkItems(workItems, nowRunningSameStudy);
 }
 private IEnumerable<WorkItem> GetPendingStudyWorkItemsScheduledBeforeOrHigherPriority(WorkItem workItem)
 {
     var workItems = GetStudyWorkItemsScheduledBeforeOrHigherPriority(workItem, WorkItemStatusEnum.Pending);
     //The stuff now running is not Pending anymore.
     return ExcludeNowRunningItems(workItems);
 }
        /// <summary>
        /// Postpone a <see cref="WorkItem"/>
        /// </summary>
        private void Postpone(WorkItem item)
        {
            DateTime now = Platform.Time;

            var timeWindowRequest = item.Request as IWorkItemRequestTimeWindow;

            if (timeWindowRequest != null && item.Request.Priority != WorkItemPriorityEnum.Stat)
            {
                DateTime scheduledTime = timeWindowRequest.GetScheduledTime(now, WorkItemServiceSettings.Default.PostponeSeconds);
                item.ProcessTime = scheduledTime;
                item.ScheduledTime = scheduledTime;
            }
            else
            {
                item.ProcessTime = now.AddSeconds(WorkItemServiceSettings.Default.PostponeSeconds);
            }

            if (item.ProcessTime > item.ExpirationTime)
                item.ExpirationTime = item.ProcessTime;
        }