Project.QueueUpdateProject method
Updates entities in a checked-out project. Also adds, modifies, or deletes custom field values.
Namespace: WebSvcProject
Assembly: ProjectServerServices (in ProjectServerServices.dll)
Syntax
'Declaration
<SoapDocumentMethodAttribute("https://schemas.microsoft.com/office/project/server/webservices/Project/QueueUpdateProject", RequestNamespace := "https://schemas.microsoft.com/office/project/server/webservices/Project/", _
ResponseNamespace := "https://schemas.microsoft.com/office/project/server/webservices/Project/", _
Use := SoapBindingUse.Literal, ParameterStyle := SoapParameterStyle.Wrapped)> _
Public Sub QueueUpdateProject ( _
jobUid As Guid, _
sessionUid As Guid, _
dataset As ProjectDataSet, _
validateOnly As Boolean _
)
'Usage
Dim instance As Project
Dim jobUid As Guid
Dim sessionUid As Guid
Dim dataset As ProjectDataSet
Dim validateOnly As Boolean
instance.QueueUpdateProject(jobUid, sessionUid, _
dataset, validateOnly)
[SoapDocumentMethodAttribute("https://schemas.microsoft.com/office/project/server/webservices/Project/QueueUpdateProject", RequestNamespace = "https://schemas.microsoft.com/office/project/server/webservices/Project/",
ResponseNamespace = "https://schemas.microsoft.com/office/project/server/webservices/Project/",
Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Wrapped)]
public void QueueUpdateProject(
Guid jobUid,
Guid sessionUid,
ProjectDataSet dataset,
bool validateOnly
)
Parameters
jobUid
Type: System.GuidThe GUID of the queue job.
sessionUid
Type: System.GuidThe GUID of the session in which the queue job is submitted.
dataset
Type: WebSvcProject.ProjectDataSetContains the project entities to update.
validateOnly
Type: System.BooleanIf true, only validates the input data and does not perform the action.
Remarks
QueueUpdateProject does not create or delete project entities; it modifies existing entities such as tasks, assignments, and project resources. QueueUpdateProject can also add, modify, or delete custom field values in a project, but cannot create or delete a custom field itself (use CreateCustomFields or DeleteCustomFields). QueueUpdateProject is an asynchronous method that sends a message to the Project Server Queuing Service.
The Project class methods, such as QueueUpdateProject, cannot create, edit, or delete cost resources. If the ProjectDataSet in the dataset parameter includes a cost resource, the method returns the ProjectCannotEditCostResource error 1050. You can use the CreateResources method to create cost resources, but Resource class methods cannot edit them. For more information, see What the PSI does and does not do.
Note
When you create or update a project, the PSI can process up to 1000 rows of data at the same time. If the total number of rows of new or updated data in all tables of ProjectDataSet exceeds 1000, the PSI returns the ProjectExceededItemsLimit error.
When creating a ProjectDataSet.TaskRow, you must specify TASK_DUR_FMT. Otherwise, later use of the project in Project Professional can result in unpredictable behavior, including possible data loss.
Any changes made to enterprise resource properties in ProjectDataSet.ProjectResourceRow will be lost the next time Project Professional refreshes the data from Project Server.
When you modify a task in a ProjectDataSet, do not set the TASK_WBS property. The TASK_WBS property is read-only, although it is marked as read/write in the PSI. If you add a task with the TASK_WBS property set to a specified value, Project Professional ignores the value set from the PSI and assigns a value according to the task outline position when you open the project. To see the result in Project Professional, check the WBS code value on the Advanced tab of the Task Information dialog box.
QueueUpdateProject cannot change a a null reference (Nothing in Visual Basic) task to a real task. For example, if you create tasks by using Project Professional and leave one or more empty lines between some of the tasks, the empty lines are a null reference (Nothing in Visual Basic) tasks.
Changing the TASK_IS_ACTIVE Property
The Project Server scheduling engine can show inconsistent start or finish times when you use the QueueUpdateProject method to change the active status of a task, if there are multiple changes in the ProjectDataSet object for the dataset parameter. If the TASK_IS_ACTIVE property is the only change in the dataset parameter, you can update the project.
For more information, see the Project Scheduling on the Server section in Project Server programmability.
Changing the TASK_OUTLINE_LEVEL Property
If you try to change the TASK_OUTLINE_LEVEL, you can get a ProjectSchedulingEngineException error from the Project Server Queuing Service. The error contents include exception="Microsoft.Office.Project.Scheduling.SchedulingCycleException: Cycle detected … . The Project Server scheduling engine does not handle bulk edits where you change the TASK_OUTLINE_LEVEL or change a task with a Start-to-Finish (SF) link into a summary task. A workaround is to check the Project Server Queue and handle the specific value in the QueueStatusDataSet.Status table. The following example modifies the WaitForQueueJobCompletion method in the ProjTool application (see Using the ProjTool Test Application). The modification uses ReadJobStatus in the QueueSystem web service and shows an appropriate message.
SvcQueueSystem.QueueStatusDataSet queueStatusDataSet =
new SvcQueueSystem.QueueStatusDataSet();
. . .
queueStatusDataSet = queueSystem.ReadJobStatus(queueStatusRequestDataSet, false,
SvcQueueSystem.SortColumn.Undefined, SvcQueueSystem.SortOrder.Undefined);
foreach (SvcQueueSystem.QueueStatusDataSet.StatusRow statusRow in queueStatusDataSet.Status)
{
if ((statusRow["ErrorInfo"] != System.DBNull.Value
&& checkStatusRowHasError(statusRow["ErrorInfo"].ToString()) == true)
|| statusRow.JobCompletionState == blockedState
|| statusRow.JobCompletionState == failedState)
{
if (statusRow.ErrorInfo.Contains("SchedulingCycleException"))
{
string schedulingError =
"The Project Server Queue reported an error in the scheduling engine.\n";
scheculingError += "The scheduling engine cannot change the TASK_OUTLINE_LEVEL\n";
schedulingError += "or change a task with a Start-to-Finish (SF) link into a summary task.\n";
schedulingError += "Use Project Professional to make those types of changes.";
MessageBox.Show(schedulingError, "Queue Error",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
MessageBox.Show(AppendErrorString(statusRow.ErrorInfo), "Queue Error" ,
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
. . .
}
Deleting Custom Field Values
Use QueueUpdateProject instead of QueueDeleteFromProject to delete custom field values from a project. The correct way to delete a custom field value is to get a ProjectDataSet, set the RowState property of a custom field DataRow to Deleted, and then use the modified ProjectDataSet to update the project. To set a DataRow to Deleted, call the Delete method on the row object rather than setting the value to a null reference (Nothing in Visual Basic).
To use the following sample code in a test application, create a task custom field of type text, and then create a project with one task. Assign a value to the custom field in the task properties, and then find the GUID values of the project and the task custom field. If you are working on a test installation of Project Server, you can use ProjTool to easily find the PROJ_UID of a project. Select the project in ProjTool, click Read Project Details, and then click the TaskCustomFields tab to find the CUSTOM_FIELD_UID. For more information about ProjTool, see Using the ProjTool Test Application.
// Sample project and task custom field GUIDs:
Guid projectId = new Guid("B6064244-101A-4139-A2F8-697620458AAE");
Guid taskCustomFieldId = new Guid("a3549fbc-b49c-42c9-9c56-ba045e438d94");
Guid sessionId = Guid.NewGuid();
Guid jobId = Guid.NewGuid();
WebSvcProject.ProjectDataSet dsProject =
project.ReadProject(projectId, WebSvcProject.DataStoreEnum.WorkingStore);
// Do not use QueueDeleteFromProject to delete a custom field.
// Guid[] taskCustomFields = { taskCustomFieldId };
// project.QueueDeleteFromProject(jobId, sessionId, projectId, taskCustomFields);
bool deleteCF = false;
foreach (WebSvcProject.ProjectDataSet.TaskCustomFieldsRow taskCFRow in dsProject.TaskCustomFields)
{
if ((Guid)taskCFRow[dsProject.TaskCustomFields.CUSTOM_FIELD_UIDColumn] == taskCustomFieldId)
{
// Set the rowstate to be deleted.
taskCFRow.Delete();
deleteCF = true;
break;
}
}
if (deleteCF)
{
project.CheckOutProject(projectId, sessionId, "Test checkout");
bool validateOnly = false;
project.QueueUpdateProject(jobId, sessionId, dsProject, validateOnly);
// Wait approximately four seconds for the queue to finish.
// Or, add a routine that checks the QueueSystem for job completion.
System.Threading.Thread.Sleep(4000);
sessionId = Guid.NewGuid();
jobId = Guid.NewGuid();
bool force = false;
string sessionDescription = "Removed task custom field " + taskCustomFieldId.ToString();
project.QueueCheckInProject(jobId, projectId, force, sessionId, sessionDescription);
// Wait approximately four seconds for queue to finish.
// Or, use a routine that checks the QueueSystem for job completion.
System.Threading.Thread.Sleep(4000);
}
Project Server Permissions
Permission |
Description |
---|---|
Allows a user to save the specified project. Category permission. |
|
Allows a user to create and save a project as an enterprise project template. |
Examples
The following example creates a sample project, checks it out, modifies a task name, saves the update, and then checks the project back in.
For critical information about running this code sample, see Prerequisites for ASMX-based code samples in Project 2013.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Web.Services.Protocols;
using System.Threading;
using PSLibrary = Microsoft.Office.Project.Server.Library;
namespace Microsoft.SDK.Project.Samples.QueueUpdateProject
{
class Program
{
[STAThread]
static void Main()
{
try
{
#region Setup
const string PROJECT_SERVER_URI = "https://ServerName/ProjectServerName/";
const string PROJECT_SERVICE_PATH = "_vti_bin/psi/project.asmx";
const string QUEUESYSTEM_SERVICE_PATH = "_vti_bin/psi/queuesystem.asmx";
const string SESSION_DESC = "Sample utility";
Guid sessionId = Guid.NewGuid();
Guid jobId;
// Set up the web service objects.
SvcProject.Project projectSvc = new SvcProject.Project();
projectSvc.Url = PROJECT_SERVER_URI + PROJECT_SERVICE_PATH;
projectSvc.Credentials = CredentialCache.DefaultCredentials;
SvcQueueSystem.QueueSystem q = new SvcQueueSystem.QueueSystem();
q.Url = PROJECT_SERVER_URI + QUEUESYSTEM_SERVICE_PATH;
q.Credentials = CredentialCache.DefaultCredentials;
// Create the sample project.
Console.WriteLine("Creating sample project");
Guid projectId = CreateSampleProject(projectSvc, q);
// Read the project that you want.
Console.WriteLine("Reading project from database");
SvcProject.ProjectDataSet projectDs = projectSvc.ReadProject(projectId, SvcProject.DataStoreEnum.WorkingStore);
#endregion
#region Change task name and update
// Check out the project.
Console.WriteLine("Checking out project");
projectSvc.CheckOutProject(projectId, sessionId, SESSION_DESC);
// Make changes.
// Note: Task 0 is the summary task, which cannot be changed.
projectDs.Task[1].TASK_NAME += " Changed";
// Save the changes.
Console.WriteLine("Saving changes to the database");
jobId = Guid.NewGuid();
projectSvc.QueueUpdateProject(jobId, sessionId, projectDs, false);
WaitForQueue(q, jobId);
#endregion
#region Check in
// Check in the project.
Console.WriteLine("Checking in the project");
jobId = Guid.NewGuid();
projectSvc.QueueCheckInProject(jobId, projectId, false, sessionId, SESSION_DESC);
WaitForQueue(q, jobId);
#endregion
}
#region Exception Handling and Final
catch (SoapException ex)
{
PSLibrary.PSClientError error = new PSLibrary.PSClientError(ex);
PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();
string errMess = "==============================\r\nError: \r\n";
for (int i = 0; i < errors.Length; i++)
{
errMess += "\n" + ex.Message.ToString() + "\r\n";
errMess += "".PadRight(30, '=') + "\r\nPSCLientError Output:\r\n \r\n";
errMess += errors[i].ErrId.ToString() + "\n";
for (int j = 0; j < errors[i].ErrorAttributes.Length; j++)
{
errMess += "\r\n\t" + errors[i].ErrorAttributeNames()[j] + ": " + errors[i].ErrorAttributes[j];
}
errMess += "\r\n".PadRight(30, '=');
}
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errMess);
}
catch (WebException ex)
{
string errMess = ex.Message.ToString() +
"\n\nLog on, or check the Project Server Queuing Service";
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Error: " + errMess);
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Error: " + ex.Message);
}
finally
{
Console.ResetColor();
Console.WriteLine("\r\n\r\nPress any key...");
Console.ReadKey();
}
#endregion
}
static private void WaitForQueue(SvcQueueSystem.QueueSystem q, Guid jobId)
{
SvcQueueSystem.JobState jobState;
const int QUEUE_WAIT_TIME = 2; // two seconds
bool jobDone = false;
string xmlError = string.Empty;
int wait = 0;
// Wait for the project to get through the queue.
// Get the estimated wait time in seconds.
wait = q.GetJobWaitTime(jobId);
// Wait for it.
Thread.Sleep(wait * 1000);
// Wait until it is finished.
do
{
// Get the job state.
jobState = q.GetJobCompletionState(jobId, out xmlError);
if (jobState == SvcQueueSystem.JobState.Success)
{
jobDone = true;
}
else
{
if (jobState == SvcQueueSystem.JobState.Unknown
|| jobState == SvcQueueSystem.JobState.Failed
|| jobState == SvcQueueSystem.JobState.FailedNotBlocking
|| jobState == SvcQueueSystem.JobState.CorrelationBlocked
|| jobState == SvcQueueSystem.JobState.Canceled)
{
// If the job failed, error out.
throw (new ApplicationException("Queue request " + jobState + " for Job ID " + jobId + ".\r\n" + xmlError));
}
else
{
Console.WriteLine("Job State: " + jobState + " for Job ID: " + jobId);
Thread.Sleep(QUEUE_WAIT_TIME * 1000);
}
}
}
while (!jobDone);
}
static private Guid CreateSampleProject(SvcProject.Project projectSvc, SvcQueueSystem.QueueSystem q)
{
SvcProject.ProjectDataSet projectDs = new SvcProject.ProjectDataSet();
Guid jobId;
// Create the project.
SvcProject.ProjectDataSet.ProjectRow projectRow = projectDs.Project.NewProjectRow();
projectRow.PROJ_UID = Guid.NewGuid();
projectRow.PROJ_NAME = "Its a wonderful project at " +
DateTime.Now.ToShortDateString().Replace("/", "") + " " +
DateTime.Now.ToShortTimeString().Replace(":", "");
projectRow.PROJ_TYPE = (int)PSLibrary.Project.ProjectType.Project;
projectDs.Project.AddProjectRow(projectRow);
// Add some tasks.
SvcProject.ProjectDataSet.TaskRow taskOne = projectDs.Task.NewTaskRow();
taskOne.PROJ_UID = projectRow.PROJ_UID;
taskOne.TASK_UID = Guid.NewGuid();
// The Task Duration format must be specified.
taskOne.TASK_DUR_FMT = (int)PSLibrary.Task.DurationFormat.Day;
taskOne.TASK_DUR = 4800; // 8 hours in duration units (minute/10)
taskOne.TASK_NAME = "Task One";
taskOne.TASK_START_DATE = System.DateTime.Now.AddDays(1);
projectDs.Task.AddTaskRow(taskOne);
SvcProject.ProjectDataSet.TaskRow taskTwo = projectDs.Task.NewTaskRow();
taskTwo.PROJ_UID = projectRow.PROJ_UID;
taskTwo.TASK_UID = Guid.NewGuid();
// The Task Duration format must be specified.
taskTwo.TASK_DUR_FMT = (int)PSLibrary.Task.DurationFormat.Day;
taskTwo.TASK_DUR = 4800; // 8 hours in duration units (minute/10)
taskTwo.TASK_NAME = "Task Two";
taskTwo.TASK_START_DATE = System.DateTime.Now.AddDays(1);
projectDs.Task.AddTaskRow(taskTwo);
// Save the project to the database.
jobId = Guid.NewGuid();
projectSvc.QueueCreateProject(jobId, projectDs, false);
WaitForQueue(q, jobId);
return projectRow.PROJ_UID;
}
}
}