ResourcePlan.QueueUpdateResourcePlan 方法
命名空间: WebSvcResourcePlan
程序集: ProjectServerServices(位于 ProjectServerServices.dll 中)
语法
声明
<SoapDocumentMethodAttribute("https://schemas.microsoft.com/office/project/server/webservices/ResourcePlan/QueueUpdateResourcePlan", RequestNamespace := "https://schemas.microsoft.com/office/project/server/webservices/ResourcePlan/", _
ResponseNamespace := "https://schemas.microsoft.com/office/project/server/webservices/ResourcePlan/", _
Use := SoapBindingUse.Literal, ParameterStyle := SoapParameterStyle.Wrapped)> _
Public Sub QueueUpdateResourcePlan ( _
projectUid As Guid, _
rpds As ResourcePlanDataSet, _
timephasedFTE As Boolean, _
autoCheckIn As Boolean, _
jobUid As Guid _
)
用法
Dim instance As ResourcePlan
Dim projectUid As Guid
Dim rpds As ResourcePlanDataSet
Dim timephasedFTE As Boolean
Dim autoCheckIn As Boolean
Dim jobUid As Guid
instance.QueueUpdateResourcePlan(projectUid, _
rpds, timephasedFTE, autoCheckIn, _
jobUid)
[SoapDocumentMethodAttribute("https://schemas.microsoft.com/office/project/server/webservices/ResourcePlan/QueueUpdateResourcePlan", RequestNamespace = "https://schemas.microsoft.com/office/project/server/webservices/ResourcePlan/",
ResponseNamespace = "https://schemas.microsoft.com/office/project/server/webservices/ResourcePlan/",
Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Wrapped)]
public void QueueUpdateResourcePlan(
Guid projectUid,
ResourcePlanDataSet rpds,
bool timephasedFTE,
bool autoCheckIn,
Guid jobUid
)
参数
projectUid
类型:System.Guid包含项目的 GUID。
rpds
类型:WebSvcResourcePlan.ResourcePlanDataSet指定更改指定的资源计划中的ResourcePlanDataSet 。
timephasedFTE
类型:System.Boolean如果true,使用分段全职等效资源进行更新。
autoCheckIn
类型:System.Boolean如果更新后的true,签入该资源计划。
jobUid
类型:System.Guid包含队列作业的 GUID。
示例
该示例在 ProjectServerServices.dll 代理服务器程序集使用SvcResourcePlan命名空间。方法SetResPlanClientEndPoints、 SetQueueClientEndPoints、 SetProjectClientEndPoints和SetResourceClientEndPoints使用的 app.config 文件来设置 WCF 绑定、 行为和终结点。有关创建 PSI 的代理服务器程序集和一个 app.config 文件的信息,请参阅Project 2013 中基于 WCF 的代码示例的先决条件。
备注
下面的示例演示如何使用QueueUpdateResourcePlan方法中;它不是一个完整的解决方案。
有关如何创建资源计划的示例代码,请参阅QueueCreateResourcePlan。资源计划已创建并发布之后, GenerateResoucePlan方法调用QueueUpdateResourcePlan方法来更新资源计划。在此示例中,资源的预订类型进行修改和更新计划。更新ResourcePlanDataSet中的内容写入 XML 文件UpdatedResourcePlanDS.xml,并如下。
<?xml version="1.0" standalone="yes"?>
<ResourcePlanDataSet xmlns="https://schemas.microsoft.com/office/project/server/webservices/ResourcePlanDataSet/">
<Utilization>
<PROJ_UID>66778c9a-b0dd-46e1-82df-611f9912a8a2</PROJ_UID>
<RESPLAN_UTILIZATION_TYPE>0</RESPLAN_UTILIZATION_TYPE>
<RESPLAN_START_DATE>2010-09-21T08:00:00-07:00</RESPLAN_START_DATE>
<RESPLAN_FINISH_DATE>2010-10-05T17:00:00-07:00</RESPLAN_FINISH_DATE>
</Utilization>
<PlanResources>
<RES_UID>39d4b541-4eb7-419f-9121-b8265e172e48</RES_UID>
<PROJ_UID>66778c9a-b0dd-46e1-82df-611f9912a8a2</PROJ_UID>
<ASSN_UID>ea599a60-13c8-4e88-9417-a1845e263185</ASSN_UID>
<RES_NAME>Res Name 39d4b541-4eb7-419f-9121-b8265e172e48</RES_NAME>
<RES_TYPE>2</RES_TYPE>
<RES_CAN_LEVEL>true</RES_CAN_LEVEL>
<RES_TIMESHEET_MGR_UID>39d4b541-4eb7-419f-9121-b8265e172e48
</RES_TIMESHEET_MGR_UID>
<RES_DEF_ASSN_OWNER>39d4b541-4eb7-419f-9121-b8265e172e48
</RES_DEF_ASSN_OWNER>
<RES_INITIALS>R</RES_INITIALS>
<RES_ID>88</RES_ID>
<ASSN_BOOKING_TYPE>1</ASSN_BOOKING_TYPE>
<RES_IS_TEAM>false</RES_IS_TEAM>
<Interval0>4800</Interval0>
<Interval1>4800</Interval1>
</PlanResources>
<PlanResources>
<RES_UID>aa74ffa6-1286-4a30-a0e1-6d48392b9a88</RES_UID>
<PROJ_UID>66778c9a-b0dd-46e1-82df-611f9912a8a2</PROJ_UID>
<ASSN_UID>3642097f-92d4-4d3b-9754-f9c597139f97</ASSN_UID>
<RES_NAME>Res Name aa74ffa6-1286-4a30-a0e1-6d48392b9a88
</RES_NAME>
<RES_TYPE>2</RES_TYPE>
<RES_CAN_LEVEL>true</RES_CAN_LEVEL>
<RES_TIMESHEET_MGR_UID>aa74ffa6-1286-4a30-a0e1-6d48392b9a88</RES_TIMESHEET_MGR_UID>
<RES_DEF_ASSN_OWNER>aa74ffa6-1286-4a30-a0e1-6d48392b9a88</RES_DEF_ASSN_OWNER>
<RES_INITIALS>R</RES_INITIALS>
<RES_ID>87</RES_ID>
<ASSN_BOOKING_TYPE>0</ASSN_BOOKING_TYPE>
<RES_IS_TEAM>false</RES_IS_TEAM>
<Interval0>4800</Interval0>
<Interval1>4800</Interval1>
</PlanResources>
<Dates>
<StartDate>2010-09-21T00:00:00-07:00</StartDate>
<EndDate>2010-10-01T00:00:00-07:00</EndDate>
<IntervalName>Interval0</IntervalName>
</Dates>
<Dates>
<StartDate>2010-10-01T00:00:00-07:00</StartDate>
<EndDate>2010-10-06T00:00:00-07:00</EndDate>
<IntervalName>Interval1</IntervalName>
</Dates>
</ResourcePlanDataSet>
完整的代码示例如下所示。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ServiceModel;
using System.Data;
using System.Xml;
using PSLibrary = Microsoft.Office.Project.Server.Library;
namespace Microsoft.SDK.Project.Samples.ResourcePlan
{
class TimephasedResourcePlan
{
private const string ENDPOINT_RESPLAN = "basicHttp_ResourcePlan";
private const string ENDPOINT_Q = "basicHttp_QueueSystem";
private const string ENDPOINT_R = "basicHttp_Resource";
private const string ENDPOINT_P = "basicHttp_Project";
private const string OUTPUT_FILES = @"C:\Project\Samples\Output\";
private const string XML_FILE1 = "ResourcePlanDS.xml";
private const string XML_FILE2 = "UpdatedResourcePlanDS.xml";
private static SvcResourcePlan.ResourcePlanClient resourcePlanClient;
private static SvcQueueSystem.QueueSystemClient queueSystemClient;
private static SvcResource.ResourceClient resourceClient;
private static SvcProject.ProjectClient projectClient;
private static string outFile_ResourcePlanDS;
private static string outFile_UpdatedResourcePlanDS;
private static List<Guid> createdResources = new List<Guid>();
private static List<Guid> createdProjects = new List<Guid>();
private static PSLibrary.ResourcePlan.TimeScale timescale =
new PSLibrary.TimeScaleClass.TimeScale();
private static int numResources = 2; // Default number of resources to create.
private static int numDays = 10; // Default number of days for the plan.
private static bool deletePlan = true; // Delete the plan, project, and resources after creating them.
static void Main(string[] args)
{
timescale = PSLibrary.TimeScaleClass.TimeScale.Months; // Default timescale.
try
{
if (args.Length > 1 && args.Length < 9)
{
if (args[0].ToLower() == "-delete")
{
deletePlan = Convert.ToBoolean(args[1]);
}
if (args.Length > 3)
{
if (args[2].ToLower() == "-timescale")
switch (args[3].ToLower())
{
case "days":
timescale = PSLibrary.TimeScaleClass.TimeScale.Days;
break;
case "quarters":
timescale = PSLibrary.TimeScaleClass.TimeScale.Quarters;
break;
case "weeks":
timescale = PSLibrary.TimeScaleClass.TimeScale.Weeks;
break;
case "years":
timescale = PSLibrary.TimeScaleClass.TimeScale.Years;
break;
default:
timescale = PSLibrary.TimeScaleClass.TimeScale.Months;
break;
}
}
if (args.Length > 5)
{
if (args[4].ToLower() == "-numresources")
numResources = Convert.ToInt32(args[5]);
}
if (args.Length > 7)
{
if (args[6].ToLower() == "-numdays")
numDays = Convert.ToInt32(args[7]);
}
}
// Configure the endpoints.
bool configResult = false;
configResult = ConfigClientEndpoints(ENDPOINT_RESPLAN);
configResult = ConfigClientEndpoints(ENDPOINT_Q);
configResult = ConfigClientEndpoints(ENDPOINT_P);
configResult = ConfigClientEndpoints(ENDPOINT_R);
if (!configResult) throw new ApplicationException();
// If output directory does not exist, create it.
if (!Directory.Exists(OUTPUT_FILES))
{
Directory.CreateDirectory(OUTPUT_FILES);
}
// Set the output file path.
outFile_ResourcePlanDS = OUTPUT_FILES + XML_FILE1;
outFile_UpdatedResourcePlanDS = OUTPUT_FILES + XML_FILE2;
try
{
bool createAssignments = true;
GenerateResourcePlan(numResources, numDays, createAssignments);
}
catch (FaultException fault)
{
// Use the WCF FaultException, because the ASMX SoapException does not
// exist in a WCF-based application.
WriteFaultOutput(fault);
}
catch (CommunicationException e)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(
"\n***System.ServiceModel.CommunicationException\n{0}:", e.Message);
Console.ResetColor();
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("\n\n***Exception:\n{0}", ex.Message);
Console.ResetColor();
}
Console.Write("\nPress any key to exit: ");
Console.ReadKey(true);
}
// Extract a PSClientError object from the WCF FaultException object, and
// then display the exception details and each error in the PSClientError stack.
private static void WriteFaultOutput(FaultException fault)
{
string errAttributeName;
string errAttribute;
string errOut;
string errMess = "".PadRight(30, '=') + "\r\n"
+ "Error details: " + "\r\n";
PSLibrary.PSClientError error = Helpers.GetPSClientError(fault, out errOut);
errMess += errOut;
if (error != null)
{
PSLibrary.PSErrorInfo[] errors = error.GetAllErrors();
PSLibrary.PSErrorInfo thisError;
for (int i = 0; i < errors.Length; i++)
{
thisError = errors[i];
errMess += "\r\n".PadRight(30, '=') + "\r\nPSClientError output:\r\n";
errMess += thisError.ErrId.ToString() + "\n";
for (int j = 0; j < thisError.ErrorAttributes.Length; j++)
{
errAttributeName = thisError.ErrorAttributeNames()[j];
errAttribute = thisError.ErrorAttributes[j];
errMess += "\r\n\t" + errAttributeName
+ ": " + errAttribute;
}
}
}
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(errMess);
Console.ResetColor();
}
/// <summary>
/// Generate the resource plan.
/// </summary>
/// <param name="resourceCount">Number of resources.</param>
/// <param name="lengthInDays">Length of the resource plan in days.</param>
/// <param name="generateAssignmentValues">True to generate assignment values.</param>
public static void GenerateResourcePlan(int resourceCount,
int lengthInDays, bool generateAssignmentValues)
{
// Create enterprise resources.
Guid[] resGuids = GenerateResources(resourceCount);
// Create a project.
Guid projUid = GenerateProject();
// Create a resource plan DataSet of the specified duration.
int daysLeft = lengthInDays;
DateTime endDate = DateTime.Now;
while (daysLeft > 0)
{
endDate = endDate.AddDays(1);
if (!(endDate.DayOfWeek == DayOfWeek.Saturday
|| endDate.DayOfWeek == DayOfWeek.Sunday))
--daysLeft;
}
// Read the resource plan.
string resPlanFilter = string.Empty;
bool fteTime = false;
bool autoCheckOut = false;
SvcResourcePlan.ResourcePlanDataSet rpds =
resourcePlanClient.ReadResourcePlan(resPlanFilter, projUid,
DateTime.Now, endDate, (short)timescale, fteTime, autoCheckOut);
// Add the resources to the resource plan DataSet.
for (int i = 0; i < resGuids.Length; i++)
{
SvcResourcePlan.ResourcePlanDataSet.PlanResourcesRow newRes =
rpds.PlanResources.NewPlanResourcesRow();
newRes.RES_UID = resGuids[i];
newRes.ASSN_BOOKING_TYPE =
(byte)PSLibrary.Resource.BookingType.Proposed;
newRes.PROJ_UID = projUid;
rpds.PlanResources.AddPlanResourcesRow(newRes);
}
// Set the resource utilization date limit.
SvcResourcePlan.ResourcePlanDataSet.UtilizationRow utilizationRow =
rpds.Utilization.NewUtilizationRow();
utilizationRow.RESPLAN_UTILIZATION_TYPE =
(int)PSLibrary.ResourcePlan.UtilizationType.FromResourcePlan;
utilizationRow.RESPLAN_UTILIZATION_DATE = new DateTime(2031, 2, 20);
utilizationRow.PROJ_UID = projUid;
if (rpds.Utilization.Count == 0)
{
rpds.Utilization.AddUtilizationRow(utilizationRow);
}
// Set assignment values.
if (generateAssignmentValues)
{
for (DateTime d = DateTime.Now; d <= endDate; d = d.AddDays(1))
{
// Set the number of hours in each workday.
double workVal = (d.DayOfWeek == DayOfWeek.Saturday ||
d.DayOfWeek == DayOfWeek.Sunday)
? 0
: 8;
string interval = GetIntervalNameByDate(d, rpds);
foreach (Guid res in resGuids)
{
SetWorkForResourceForInterval(interval, res,
projUid, workVal, rpds);
}
}
}
// Create the resource plan.
Guid jobGuid = Guid.NewGuid();
int numJobs = 1;
bool autoCheckIn = false;
resourcePlanClient.QueueCreateResourcePlan(projUid, rpds,
fteTime, autoCheckIn, jobGuid);
Helpers.WaitForQueue(SvcQueueSystem.QueueMsgType.ResourcePlanSave,
numJobs, queueSystemClient, jobGuid);
Console.WriteLine("Resource plan created successfully.");
// Publish the resource plan.
resourcePlanClient.QueuePublishResourcePlan(projUid, jobGuid);
Helpers.WaitForQueue(SvcQueueSystem.QueueMsgType.ResourcePlanPublish,
numJobs, queueSystemClient, jobGuid);
Console.WriteLine("Resource plan published.");
// Read the resource plan.
rpds = resourcePlanClient.ReadResourcePlan(resPlanFilter, projUid, DateTime.Now,
endDate, (short)timescale, fteTime, autoCheckOut);
// Write the ResourcePlan DataSet to an XML file.
rpds.WriteXml(outFile_ResourcePlanDS);
Console.WriteLine("\nSee XML output of resource plan DataSet at\n\t{0}",
outFile_ResourcePlanDS);
Console.Write("\nPress any key to continue: ");
Console.ReadKey(true);
// Change the booking type for a resource and update the resource plan.
Guid updateJob = Guid.NewGuid();
rpds.PlanResources[0].ASSN_BOOKING_TYPE =
(int)PSLibrary.Resource.BookingType.Committed;
resourcePlanClient.QueueUpdateResourcePlan(projUid, rpds,
fteTime, autoCheckIn, updateJob);
Helpers.WaitForQueue(SvcQueueSystem.QueueMsgType.ResourcePlanSave,
numJobs, queueSystemClient, jobGuid);
Console.WriteLine("Resource plan updated successfully.");
// Write the updated resource plan DataSet to an XML file.
rpds.WriteXml(outFile_UpdatedResourcePlanDS);
Console.WriteLine("\nSee XML output of the updated resource plan DataSet at \n\t{0}",
outFile_UpdatedResourcePlanDS);
if (deletePlan)
{
Console.WriteLine("\nPress any key to continue");
Console.Write("...and delete the resource plan, project, and resources:");
Console.ReadKey(true);
DeleteResourcePlan(projUid, resGuids);
}
}
// Delete the resource plan and the test project and resources.
public static void DeleteResourcePlan(Guid projUid, Guid[] resUids)
{
// QueueDeleteResourcePlan takes an array of project GUIDs and job GUIDs.
Guid deleteResPlanJob = Guid.NewGuid();
Guid[] deleteJobs = new Guid[1] { deleteResPlanJob };
Guid[] projects = new Guid[1] { projUid };
resourcePlanClient.QueueDeleteResourcePlan(projects, deleteJobs);
int numJobs = 1;
Helpers.WaitForQueue(SvcQueueSystem.QueueMsgType.ResourcePlanDelete,
numJobs, queueSystemClient, deleteResPlanJob);
Guid projDeleteJob = Guid.NewGuid();
bool deleteProjectSite = false; // No project site was created during publish.
bool deleteInPubAndDraftDb = true;
projectClient.QueueDeleteProjects(projDeleteJob, deleteProjectSite,
projects, deleteInPubAndDraftDb);
Helpers.WaitForQueue(SvcQueueSystem.QueueMsgType.ProjectDelete,
numJobs, queueSystemClient, projDeleteJob);
// Resources must be checked out before they can be deleted.
resourceClient.CheckOutResources(resUids);
string noComment = string.Empty;
resourceClient.DeleteResources(resUids, noComment);
Console.WriteLine("Resource plan, project, and resources deleted successfully.");
}
// Create the specified number of enterprise resources.
public static Guid[] GenerateResources(int numRes)
{
Console.WriteLine("Creating {0} resources:", numRes.ToString());
Guid[] resUids = new Guid[numRes];
SvcResource.ResourceDataSet rds = new SvcResource.ResourceDataSet();
for (int i = 0; i < numRes; i++)
{
SvcResource.ResourceDataSet.ResourcesRow resRow =
rds.Resources.NewResourcesRow();
resRow.RES_UID = Guid.NewGuid();
string resName = "Res Name " + resRow.RES_UID;
Console.WriteLine("\t {0}", resName);
resRow.RES_NAME = resName;
rds.Resources.AddResourcesRow(resRow);
resUids[i] = resRow.RES_UID;
}
resourceClient.CreateResources(rds, false, true);
createdResources.AddRange(resUids);
return resUids;
}
// Create a project.
public static Guid GenerateProject()
{
SvcProject.ProjectDataSet pds = new SvcProject.ProjectDataSet();
SvcProject.ProjectDataSet.ProjectRow pRow = pds.Project.NewProjectRow();
pRow.PROJ_UID = Guid.NewGuid();
string projName = "Proj " + pRow.PROJ_UID;
Console.WriteLine("Creating project: {0}", projName);
pRow.PROJ_NAME = projName;
pds.Project.AddProjectRow(pRow);
Guid jobUid = Guid.NewGuid();
projectClient.QueueCreateProject(jobUid, pds, false);
Helpers.WaitForQueue(SvcQueueSystem.QueueMsgType.ProjectCreate,
1, queueSystemClient, jobUid);
Guid joubUid = Guid.NewGuid();
string mssUrl = string.Empty; // Don't create a project site.
bool fullPublish = true;
projectClient.QueuePublish(jobUid, pRow.PROJ_UID, fullPublish, mssUrl);
Helpers.WaitForQueue(SvcQueueSystem.QueueMsgType.ProjectPublish, 1,
queueSystemClient, jobUid);
createdProjects.Add(pRow.PROJ_UID);
return pRow.PROJ_UID;
}
// Get the assignment interval name.
public static string GetIntervalNameByDate(DateTime date,
SvcResourcePlan.ResourcePlanDataSet resDS)
{
foreach (SvcResourcePlan.ResourcePlanDataSet.DatesRow row in resDS.Dates)
if (date >= row.StartDate && date < row.EndDate)
return row.IntervalName;
return string.Empty;
}
// Set work for a resource, for the specified interval.
public static void SetWorkForResourceForInterval(string intervalName,
Guid resourceGuid,
Guid projUid, double value,
SvcResourcePlan.ResourcePlanDataSet rpds)
{
SvcResourcePlan.ResourcePlanDataSet.DatesRow datesRow =
rpds.Dates.FindByIntervalName(intervalName);
if (datesRow == null)
{
Console.WriteLine("\nFailed to set value for interval: {0}. Did not find interval.",
intervalName);
return;
}
SvcResourcePlan.ResourcePlanDataSet.PlanResourcesRow planRow =
rpds.PlanResources.FindByRES_UIDPROJ_UID(resourceGuid, projUid);
planRow[intervalName] = value * PSLibrary.ValidationConst.s_cal_units_per_hour;
}
// Configure the PSI client endpoints.
public static bool ConfigClientEndpoints(string endpt)
{
bool result = true;
switch (endpt)
{
case ENDPOINT_RESPLAN:
resourcePlanClient = new SvcResourcePlan.ResourcePlanClient(endpt);
break;
case ENDPOINT_P:
projectClient = new SvcProject.ProjectClient(endpt);
break;
case ENDPOINT_Q:
queueSystemClient = new SvcQueueSystem.QueueSystemClient(endpt);
break;
case ENDPOINT_R:
resourceClient = new SvcResource.ResourceClient(endpt);
break;
default:
result = false;
Console.WriteLine("Invalid endpoint: {0}", endpt);
break;
}
return result;
}
}
class Helpers
{
/// <summary>
/// Public TimeOut property.
/// </summary>
/// <value>Gets or sets the timeout for WaitForQueue calls.</value>
public static int TimeOut
{
get
{
return timeOut;
}
set
{
timeOut = value;
}
}
// Default timeout is 3 minutes.
private static int timeOut = 3 * 60 * 1000;
// Incremental sleep time is 2 seconds.
private static int incrementalSleepTime = 2 * 1000;
public static SvcQueueSystem.JobState WaitForQueue(
SvcQueueSystem.QueueMsgType jobType, int numJobs,
SvcQueueSystem.QueueSystemClient queueSystemClient, Guid jobId)
{
int timeSlept = 0;
int sleepInterval = (TimeOut / 60 > incrementalSleepTime) ?
TimeOut / 60 : incrementalSleepTime;
SvcQueueSystem.QueueStatusDataSet queueStatusDs =
new SvcQueueSystem.QueueStatusDataSet();
String errorString = String.Empty;
Console.WriteLine("\nWaiting for job" + jobType.ToString());
while (true)
{
SvcQueueSystem.JobState jobState =
queueSystemClient.GetJobCompletionState(out errorString, jobId);
SvcQueueSystem.QueueStatusDataSet jobStatus =
queueSystemClient.ReadJobStatusSimple(new Guid[] { jobId }, true);
if (jobState == SvcQueueSystem.JobState.Unknown)
{
string jobStatusInfo = "Job status is unknown.";
jobStatusInfo += "\n\tWas the job placed on the Queue?";
jobStatusInfo += "\n\t--returning from WaitForQueue.";
Console.WriteLine(jobStatusInfo);
return jobState;
}
if (jobState == SvcQueueSystem.JobState.Success ||
jobState == SvcQueueSystem.JobState.Failed ||
jobState == SvcQueueSystem.JobState.FailedNotBlocking ||
jobState == SvcQueueSystem.JobState.CorrelationBlocked ||
jobState == SvcQueueSystem.JobState.Canceled)
{
Console.WriteLine("\tJob completed, returning from WaitForQueue");
return jobState;
}
System.Threading.Thread.CurrentThread.Join(sleepInterval);
timeSlept += sleepInterval;
if (timeSlept > TimeOut)
{
return SvcQueueSystem.JobState.Unknown;
}
}
}
/// <summary>
/// Extract a PSClientError object from the ServiceModel.FaultException,
/// for use in output of the GetPSClientError stack of errors.
/// </summary>
/// <param name="e"></param>
/// <param name="errOut">Shows that FaultException has more information
/// about the errors than PSClientError has. FaultException can also contain
/// other types of errors, such as failure to connect to the server.</param>
/// <returns>PSClientError object, for enumerating errors.</returns>
public static PSLibrary.PSClientError GetPSClientError(FaultException e,
out string errOut)
{
const string PREFIX = "GetPSClientError() returns null: ";
errOut = string.Empty;
PSLibrary.PSClientError psClientError = null;
if (e == null)
{
errOut = PREFIX + "Null parameter (FaultException e) passed in.";
psClientError = null;
}
else
{
// Get a ServiceModel.MessageFault object.
var messageFault = e.CreateMessageFault();
if (messageFault.HasDetail)
{
using (var xmlReader = messageFault.GetReaderAtDetailContents())
{
var xml = new XmlDocument();
xml.Load(xmlReader);
var serverExecutionFault = xml["ServerExecutionFault"];
if (serverExecutionFault != null)
{
var exceptionDetails = serverExecutionFault["ExceptionDetails"];
if (exceptionDetails != null)
{
try
{
errOut = exceptionDetails.InnerXml + "\r\n";
psClientError =
new PSLibrary.PSClientError(exceptionDetails.InnerXml);
}
catch (InvalidOperationException ex)
{
errOut = PREFIX + "Unable to convert fault exception info ";
errOut += "a valid Project Server error message. Message: \n\t";
errOut += ex.Message;
psClientError = null;
}
}
else
{
errOut = PREFIX + "The FaultException e is a ServerExecutionFault, "
+ "but does not have ExceptionDetails.";
}
}
else
{
errOut = PREFIX + "The FaultException e is not a ServerExecutionFault.";
}
}
}
else // No detail in the MessageFault.
{
errOut = PREFIX + "The FaultException e does not have any detail.";
}
}
errOut += "\r\n" + e.ToString() + "\r\n";
return psClientError;
}
}
}