Project.QueueUpdateProject Method
Updates entities in a checked-out project. Also adds, modifies, or deletes custom field values.
Namespace: WebSvcProject
Assembly: ProjectServerWebServices (in ProjectServerWebServices.dll)
Syntax
'Declaration
<SoapDocumentMethodAttribute("http://schemas.microsoft.com/office/project/server/webservices/Project/QueueUpdateProject", RequestNamespace := "http://schemas.microsoft.com/office/project/server/webservices/Project/", _
ResponseNamespace := "http://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("http://schemas.microsoft.com/office/project/server/webservices/Project/QueueUpdateProject", RequestNamespace = "http://schemas.microsoft.com/office/project/server/webservices/Project/",
ResponseNamespace = "http://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.Guid
GUID of the queue job.
- sessionUid
Type: System.Guid
GUID of the session in which the queue job is submitted.
- dataset
Type: WebSvcProject.ProjectDataSet
Contains the project entities to update.
- validateOnly
Type: System.Boolean
If 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.
Note
When you create or update a project, the PSI can process up to 1000 rows of data at a 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, don't 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 create tasks using Microsoft Office 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_OUTLINE_LEVEL
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.
QueueSystemWebSvc.QueueStatusDataSet queueStatusDataSet =
new QueueSystemWebSvc.QueueStatusDataSet();
. . .
queueStatusDataSet = queueSystem.ReadJobStatus(queueStatusRequestDataSet, false,
QueueSystemWebSvc.SortColumn.Undefined, QueueSystemWebSvc.SortOrder.Undefined);
foreach (QueueSystemWebSvc.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 set 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 4 seconds (more or less) for 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 4 seconds (more or less) 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 |
Save the specified project. Category permission. |
|
Can edit the specified project. Category permission. |
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.
Please see Prerequisites for Reference Code Samples for critical information on running this code sample.
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 = "http://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
ProjectWebSvc.Project projectSvc = new ProjectWebSvc.Project();
projectSvc.Url = PROJECT_SERVER_URI + PROJECT_SERVICE_PATH;
projectSvc.Credentials = CredentialCache.DefaultCredentials;
QueueSystemWebSvc.QueueSystem q = new QueueSystemWebSvc.QueueSystem();
q.Url = PROJECT_SERVER_URI + QUEUESYSTEM_SERVICE_PATH;
q.Credentials = CredentialCache.DefaultCredentials;
// Create sample project
Console.WriteLine("Creating sample project");
Guid projectId = CreateSampleProject(projectSvc, q);
// Read the project you want
Console.WriteLine("Reading project from database");
ProjectWebSvc.ProjectDataSet projectDs = projectSvc.ReadProject(projectId, ProjectWebSvc.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 can't 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(QueueSystemWebSvc.QueueSystem q, Guid jobId)
{
QueueSystemWebSvc.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 done.
do
{
// - Get the job state
jobState = q.GetJobCompletionState(jobId, out xmlError);
if (jobState == QueueSystemWebSvc.JobState.Success)
{
jobDone = true;
}
else
{
if (jobState == QueueSystemWebSvc.JobState.Unknown
|| jobState == QueueSystemWebSvc.JobState.Failed
|| jobState == QueueSystemWebSvc.JobState.FailedNotBlocking
|| jobState == QueueSystemWebSvc.JobState.CorrelationBlocked
|| jobState == QueueSystemWebSvc.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(ProjectWebSvc.Project projectSvc, QueueSystemWebSvc.QueueSystem q)
{
ProjectWebSvc.ProjectDataSet projectDs = new ProjectWebSvc.ProjectDataSet();
Guid jobId;
// Create the project
ProjectWebSvc.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
ProjectWebSvc.ProjectDataSet.TaskRow taskOne = projectDs.Task.NewTaskRow();
taskOne.PROJ_UID = projectRow.PROJ_UID;
taskOne.TASK_UID = Guid.NewGuid();
//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);
ProjectWebSvc.ProjectDataSet.TaskRow taskTwo = projectDs.Task.NewTaskRow();
taskTwo.PROJ_UID = projectRow.PROJ_UID;
taskTwo.TASK_UID = Guid.NewGuid();
//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;
}
}
}