Objective
Connect to Project Server 2013 in order to update an existing assignment of a resource to a task or create a new assignment for the resource in case it (the resource) is not assigned to the particular task.
Context info
The tasks are identified by project and by a custom field called EnterpriseAbs (more tasks can have the same value). The assignments are identified by task and resourceId. The update operation is meant only for the update of the assignment units.
Assignments can be created/updated for normal projects and also for master projects. That excludes the use of CSOM being that master projects cannot be read/updated. The idea was to use the OData Endpoint in order to do the operations. Considering the situation I added a service reference to the endpoint. It's all nice and good with querying the data using the context, but as soon as I want to make some changes I can't because:
- I can't find a way to check out the project,
- It throws an
SPException
with the message
"The security validation for this page is invalid and might be corrupted. Please use your web browser's Back button to try your operation again."
as soon as I try to save the changes.
The method that does the operation is the following:
private IHttpActionResult UpsertViaOData(Guid projectId,
string abs,
AssignmentInformation assignment,
ProjectODataContainer.ReportingData container,
ProjectContext context)
{
var tasks = container.Tasks.Where(t => t.ProjectId == projectId
&& t.EnterpriseABS == abs)
.ToList();
if (tasks.Count == 0)
{
return Ok();
}
var project = container.Projects.Where(p => p.ProjectId == projectId)
.ToList()
.FirstOrDefault();
if (project == default(ProjectODataContainer.Project))
{
return NotFound();
}
var resourceId = assignment.Resource.Id;
var assignments = container.Assignments.Where(a => a.ProjectId == projectId
&& a.ResourceId == resourceId)
.ToDictionary(a => a.TaskId, a => a);
foreach (var task in tasks)
{
ProjectODataContainer.Assignment taskAssignment;
if (assignments.TryGetValue(task.TaskId, out taskAssignment))
{
// assignment already exists
if (taskAssignment.AssignmentPeakUnits != assignment.Units)
{
taskAssignment.AssignmentPeakUnits = assignment.Units;
container.UpdateObject(taskAssignment);
}
}
else
{
// assignment does not exist
taskAssignment = new ProjectODataContainer.Assignment
{
TaskId = task.TaskId,
AssignmentId = Guid.NewGuid(),
ProjectId = projectId,
AssignmentPeakUnits = assignment.Units
};
container.AddToAssignments(taskAssignment);
}
}
// This is the instruction that throws the aforementioned Exception
container.SaveChanges(System.Data.Services.Client.SaveChangesOptions.PatchOnUpdate);
return Ok();
}
with ProjectODataContainer.ReportingData
being the context and AssignmentInformation
being defined as follows:
public class AssignmentInformation
{
public Guid Id { get; set; }
public ResourceInformation Resource { get; set; }
public Guid TaskId { get; set; }
public decimal? Units { get; set; }
public AssignmentInformation() { }
}
Seems like it is an authorization problem, but the credentials with which the context runs have the right privileges (the same credentials used with CSOM do the job, but - as I wrote before - CSOM cannot be used with master projects).
Anybody has an idea how to solve this?