Statusing.ReadStatusForResource 方法
为指定的工作分配或指定的资源的所有工作分配,请获取 Statusing 数据。不需要模拟的资源。
命名空间: WebSvcStatusing
程序集: ProjectServerServices(位于 ProjectServerServices.dll 中)
语法
声明
<SoapDocumentMethodAttribute("https://schemas.microsoft.com/office/project/server/webservices/Statusing/ReadStatusForResource", RequestNamespace := "https://schemas.microsoft.com/office/project/server/webservices/Statusing/", _
ResponseNamespace := "https://schemas.microsoft.com/office/project/server/webservices/Statusing/", _
Use := SoapBindingUse.Literal, ParameterStyle := SoapParameterStyle.Wrapped)> _
Public Function ReadStatusForResource ( _
resid As Guid, _
assnid As Guid, _
mindate As DateTime, _
maxdate As DateTime _
) As StatusingDataSet
用法
Dim instance As Statusing
Dim resid As Guid
Dim assnid As Guid
Dim mindate As DateTime
Dim maxdate As DateTime
Dim returnValue As StatusingDataSet
returnValue = instance.ReadStatusForResource(resid, _
assnid, mindate, maxdate)
[SoapDocumentMethodAttribute("https://schemas.microsoft.com/office/project/server/webservices/Statusing/ReadStatusForResource", RequestNamespace = "https://schemas.microsoft.com/office/project/server/webservices/Statusing/",
ResponseNamespace = "https://schemas.microsoft.com/office/project/server/webservices/Statusing/",
Use = SoapBindingUse.Literal, ParameterStyle = SoapParameterStyle.Wrapped)]
public StatusingDataSet ReadStatusForResource(
Guid resid,
Guid assnid,
DateTime mindate,
DateTime maxdate
)
参数
resid
类型:System.Guid资源的 GUID。
assnid
类型:System.Guid工作分配的 GUID。Guid.empty返回所有分配。
mindate
类型:System.DateTime日期范围的开始。使用DateTime.MinDate选择从头开始。
maxdate
类型:System.DateTime日期范围的末尾。使用DateTime.MaxDate指定到末尾。
返回值
类型:WebSvcStatusing.StatusingDataSet
返回StatusingDataSet。
备注
Project Server 权限
权限 |
说明 |
---|---|
允许用户可以读取和更新代表资源的状态。全局权限。 |
示例
WCF 和手动计划的任务的示例: UpdateStatus_ManualTasks示例是一个测试应用程序执行以下操作,手动计划的任务或自动计划任务:
分析项目名称、 任务名称、 数工作小时数,optionalassigned 资源和可选状态注释。
如果未不指定任何资源,则假定应用程序用户分配到任务。
获取为用户分配给指定的任务的 GUID。
获取并验证工作分配数据。假定只有一个资源分配。
创建UpdateStatus方法的changeXml字符串。对于简单的测试,将向百分比完成 (最多 100) 报告时间。如果应用程序用户指定不同的资源,将ResID属性添加到changeXml字符串中的Assn元素。
调用UpdateStatus,然后调用单个工作分配的SubmitStatusForResource,。
调用ReadStatusForResource以获取更新的StatusingDataSet。
应用程序还将写入到 XML 文件,以便在测试中使用的数据集和changeXml值。
若要使用的UpdateStatus测试应用程序,请执行以下操作:
创建测试项目、 添加两个手动计划的任务、 设置任务持续时间和开始日期,并将自己和另一个用户作为资源。将一个任务到您和其他任务分配给其他用户。
例如,测试项目项目命名为命名任务 T1 和 T2,,然后设置每个任务的工期为三天。假定您的用户名是用户 1,其他用户是用户 2。将 T1 分配给用户 1,并为用户 2 分配 T2。
For configuration of the WCF endpoints, create an app.config file. For information about using the code sample in a Microsoft Visual Studio 2010 project and creating an app.config file, see Project 2013 中基于 WCF 的代码示例的先决条件.
运行测试,使用各种参数。请参阅参数信息的Usage方法在下面的代码。例如,在命令提示符窗口中运行以下测试:
UpdateStatus -p "Test project" -t "T1" -hours 6 -c "This is a comment"
输出显示:
Updating status for User 1 on task 'T1': 6 hours Manually scheduled task
UpdateStatus -p "Test project" -t "T1" -hours 6 -r "User 2" -c "This is a comment"
用户 2 未分配到任务 T1,以便应用程序将显示The assignment on task 'T1' is for User 1, not for User 2。
UpdateStatus -p "Test project" -t "T2" -hours 6 -r "User 2" -c "This is a comment"
输出显示:
Updating status for User 2 on task 'T2': 6 hours Manually scheduled task
每个测试后,选中Project Web App状态更新中的审批中心。
备注
添加手动计划的任务没有开始日期或时间时,默认为八个工时。您仍然可以分配给任务的资源和更新状态。例如,如果的任务 T3 有无开始日期或持续时间,并且您使用的UpdateStatus测试应用程序设置的工作分配的资源的 6 个小时,则 Project Server T3 的开始日期设置为项目开始日期,,并将六个小时的实际工时。后接受状态更新,您可以添加实际工时和剩余工时列Project Professional 2010中的甘特图视图。T3 的工作是八个小时、 实际工时是六个小时、 和剩余工时是两个小时。如果您添加到资源使用状况视图或任务分配状况视图中的详细信息窗格的实际工时行,您还可以参见六个小时的实际工时。
完整的 Visual Studio 解决方案是 SDK 下载中Project 2010 。
using System;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Security.Principal;
using System.Xml;
using PSLibrary = Microsoft.Office.Project.Server.Library;
namespace Microsoft.SDK.Project.Samples.UpdateStatus
{
class Program
{
private const string ENDPOINT_PROJECT = "basicHttp_Project";
private const string ENDPOINT_RESOURCE = "basicHttp_Resource";
private const string ENDPOINT_STATUSING = "basicHttp_Statusing";
// Change the output directory for your computer.
private const string OUTPUT_FILES = @"C:\Project\Samples\Output\";
private const string XML_FILE1 = "UpdateStatus_jc_BasicProjectInfo.xml";
private const string XML_FILE2 = "UpdateStatus_jc_FullProjectInfo.xml";
private const string XML_FILE3 = "UpdateStatus_jc_ChangeXml.xml";
private const string XML_FILE4 = "UpdateStatus_jc_NewStatus.xml";
private static SvcProject.ProjectClient projectClient;
private static SvcResource.ResourceClient resourceClient;
private static SvcStatusing.StatusingClient statusingClient;
private static string projectName = string.Empty; // Project name.
private static string taskName = string.Empty; // Task name.
private static string resName = string.Empty; // Assigned resource name.
private static string requestedResName = string.Empty; // Requested resource name.
private static string appUserName = string.Empty; // Application user name.
private static DateTime minDate = DateTime.Today; // Minimum date for statusing data.
private static DateTime maxDate = DateTime.Today; // Maximum date for statusing data.
private static decimal hoursWorked = 0.0M; // Number of hours worked on the assignment.
private static string comment = string.Empty; // Comment for status submission.
private static bool showUsage = false;
private static StreamWriter writer;
static void Main(string[] args)
{
string outFilePath1 = OUTPUT_FILES + XML_FILE1;
string outFilePath2 = OUTPUT_FILES + XML_FILE2;
string outFilePath3 = OUTPUT_FILES + XML_FILE3;
string outFilePath4 = OUTPUT_FILES + XML_FILE4;
writer = new StreamWriter(outFilePath3);
Guid projUid = Guid.Empty; // GUID of the project.
Guid taskUid = Guid.Empty; // GUID of the task.
Guid assnUid = Guid.Empty; // GUID of the assignment.
Guid resUid = Guid.Empty; // GUID of the assigned resource.
Guid requestedResUid = Guid.Empty; // GUID of the requested resource, for submitting status.
Guid myUid = Guid.Empty; // GUID of the application user.
double regularWork = 0; // Assignment working time at the regular rate,
// in thousandths of minutes.
bool isTaskManual = false; // Specifies whether the task is manually scheduled.
if (!ParseCommandLine(args))
{
Usage();
ExitApp();
}
try
{
ConfigClientEndpoints(ENDPOINT_PROJECT);
ConfigClientEndpoints(ENDPOINT_RESOURCE);
ConfigClientEndpoints(ENDPOINT_STATUSING);
WindowsIdentity winId = WindowsIdentity.GetCurrent();
appUserName = winId.Name;
myUid = resourceClient.GetCurrentUserUid();
Console.WriteLine("User: {0}; PWA GUID: {1}", appUserName, myUid.ToString());
// Get the basic information for the project.
SvcProject.ProjectDataSet projectDs = projectClient.ReadProjectStatus(
Guid.Empty, SvcProject.DataStoreEnum.PublishedStore, projectName,
(int)PSLibrary.Project.ProjectType.Project);
Console.WriteLine("XML output for ReadProjectStatus:\n\t{0}",
outFilePath1);
projectDs.WriteXml(outFilePath1);
projUid = projectDs.Project[0].PROJ_UID;
if (projUid == Guid.Empty)
throw (new ArgumentException(
string.Format("\nThe project '{0}' does not exist.", projectName)));
// Get the full information for the project.
projectDs = projectClient.ReadProject(projUid,
SvcProject.DataStoreEnum.PublishedStore);
Console.WriteLine("XML output for ReadProject:\n\t{0}", outFilePath2);
projectDs.WriteXml(outFilePath2);
bool isCurrentUser; // Specifies whether the assignment is for the current user.
// Get requested resource GUID. Exit if the requested resource is not a project resource.
if (string.IsNullOrEmpty(requestedResName))
{
// The current user is the requested resource.
isCurrentUser = true;
// Use a Linq query over the typed ProjectDataSet to check whether
// the app user is a resource on the project.
var query = from r in projectDs.ProjectResource
where r.RES_UID == myUid
select new { r.RES_NAME };
foreach (var resRow in query)
{
requestedResName = resRow.RES_NAME;
requestedResUid = myUid;
}
}
else
{
isCurrentUser = false;
var query = from r in projectDs.ProjectResource
where r.RES_NAME == requestedResName
select new { r.RES_UID };
foreach (var resRow in query)
{
requestedResUid = resRow.RES_UID;
}
}
if (requestedResUid == Guid.Empty)
{
if (string.IsNullOrEmpty(requestedResName))
requestedResName = appUserName;
throw (new ArgumentException(
string.Format("\nThe resource '{0}' is not on the project '{1}'",
requestedResName, projectName)));
}
// Get the assignment data for the task.
for (int i = 0; i < projectDs.Assignment.Count; i++)
{
if (projectDs.Assignment[i].TASK_NAME == taskName)
{
taskUid = projectDs.Assignment[i].TASK_UID;
assnUid = projectDs.Assignment[i].ASSN_UID;
resUid = projectDs.Assignment[i].RES_UID;
regularWork = projectDs.Assignment[i].ASSN_WORK;
minDate = projectDs.Assignment[i].ASSN_START_DATE;
maxDate = projectDs.Assignment[i].ASSN_FINISH_DATE;
// Get the assigned resource name.
for (int j = 0; j < projectDs.ProjectResource.Count; j++)
{
if (projectDs.ProjectResource[j].RES_UID == resUid)
{
resName = projectDs.ProjectResource[j].RES_NAME;
break;
}
}
// Is the task manually scheduled?
for (int t = 0; t < projectDs.Task.Count; t++)
{
if (projectDs.Task[t].TASK_UID == taskUid)
{
isTaskManual = projectDs.Task[t].TASK_IS_MANUAL;
break;
}
}
break;
}
}
ValidateAssignment(isCurrentUser, resUid, myUid, assnUid, requestedResUid);
string changeXml = CreateChangeXml(projUid, assnUid, resUid,
hoursWorked, regularWork, isCurrentUser);
Console.WriteLine("XML output for ChangeXml:\n\t{0}", outFilePath3);
writer.WriteLine(changeXml);
Console.ForegroundColor = ConsoleColor.Yellow;
string yellowString = "\nUpdating status for {0} on task '{1}': {2} hours\n\t";
yellowString += (isTaskManual) ? "Manually scheduled task" : "Auto-scheduled task";
Console.WriteLine(yellowString, resName, taskName, hoursWorked);
Console.ResetColor();
statusingClient.UpdateStatus(changeXml);
Guid[] assignments = { assnUid };
statusingClient.SubmitStatusForResource(resUid, assignments, comment);
SvcStatusing.StatusingDataSet statusingDs =
statusingClient.ReadStatusForResource(resUid, assnUid, minDate, maxDate);
Console.WriteLine("\nXML output for ReadStatusForResource:\n\t{0}", outFilePath4);
statusingDs.WriteXml(outFilePath4);
}
catch (FaultException fault)
{
// Use the WCF FaultException, because the ASMX SoapException does not
// exist in a WCF-based application.
Console.ForegroundColor = ConsoleColor.Red;
WriteFaultOutput(fault);
Console.WriteLine();
showUsage = true;
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.Message);
Console.WriteLine();
showUsage = true;
}
finally
{
writer.Close();
Console.ResetColor();
if (showUsage) Usage();
ExitApp();
}
}
// Validate the assignment and the resource.
private static void ValidateAssignment(bool isCurrentUser, Guid resUid, Guid myUid,
Guid assnUid, Guid requestedResUid)
{
if (!isCurrentUser && resUid == myUid)
throw (new ArgumentException(
string.Format("\nThe assignment on task '{0}' is for {1}, not for {2}",
taskName, resName, requestedResName)));
if (assnUid == Guid.Empty)
throw (new ArgumentException(
string.Format("\nNo assignment for {0} on task '{0}'", resName, taskName)));
if (requestedResUid != resUid)
throw (new ArgumentException(
string.Format("\nResource '{0}' does not match assignment on task '{1}'",
requestedResName, taskName)));
}
/// <summary>Create a ChangeXml string for the UpdateStatus method.</summary>
/// <param name="projUid">Project GUID</param>
/// <param name="assnUid">Assignment GUID</param>
/// <param name="resUid">Resource GUID</param>
/// <param name="hoursWorked">Number of hours worked.</param>
/// <param name="totalRegHours">
/// Total assigned hours at the regular rate, in thousandths of minutes.</param>
/// <param name="isCurrentUser">True if the assignment is for the current user.</param>
/// <returns></returns>
private static string CreateChangeXml(Guid projUid, Guid assnUid, Guid resUid,
decimal hoursWorked, double totalRegHours, bool isCurrentUser)
{
StringBuilder cXmlBuilder = new StringBuilder("<Changes>");
cXmlBuilder.AppendFormat("<Proj ID=\"{0}\">", projUid.ToString());
cXmlBuilder.AppendFormat("<Assn ID=\"{0}\"", assnUid.ToString());
// If the assignment is not for the current user, add the ResID attribute,
// and then close the Assn element.
if (isCurrentUser)
cXmlBuilder.Append(">");
else
cXmlBuilder.AppendFormat(" ResID=\"{0}\">", resUid.ToString());
// Specify the process ID (PID) for percent work complete.
// The "Supported Project Fields and Field Information for Statusing ChangeXML"
// SDK article shows the PID value is 251658274.
string pidPctWorkComplete = PSLibrary.AssnConstID.s_apid_pct_wrk_complete.ToString(
CultureInfo.InvariantCulture);
// Calculate the percent work complete.
int pctComplete = Convert.ToInt32(100 * hoursWorked /
Convert.ToDecimal(totalRegHours / 1000 / 60));
if (pctComplete > 100) pctComplete = 100;
cXmlBuilder.AppendFormat("<Change PID=\"{0}\">{1}</Change>", pidPctWorkComplete,
pctComplete.ToString());
cXmlBuilder.Append("</Assn></Proj></Changes>");
return cXmlBuilder.ToString();
}
// 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;
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();
}
// Use the endpoints defined in app.config to configure the client.
public static void ConfigClientEndpoints(string endpt)
{
if (endpt == ENDPOINT_PROJECT)
projectClient = new SvcProject.ProjectClient(endpt);
else if (endpt == ENDPOINT_RESOURCE)
resourceClient = new SvcResource.ResourceClient(endpt);
else if (endpt == ENDPOINT_STATUSING)
statusingClient = new SvcStatusing.StatusingClient(endpt);
}
// Parse the command line. Return true if there are no errors.
private static bool ParseCommandLine(string[] args)
{
int i;
bool error = false;
int argsLength = args.Length;
for (i = 0; i < argsLength; i++)
{
if (error) break;
if (args[i].StartsWith("-") || args[i].StartsWith("/"))
args[i] = "*" + args[i].Substring(1).ToLower();
switch (args[i])
{
case "*projectname":
case "*p":
if (++i >= argsLength) return false;
projectName = args[i];
break;
case "*taskname":
case "*t":
if (++i >= argsLength) return false;
taskName = args[i];
break;
case "*resourcename":
case "*r":
if (++i >= argsLength) return false;
requestedResName = args[i];
break;
case "*hours":
case "*h":
if (++i >= argsLength) return false;
hoursWorked = Convert.ToDecimal(args[i]);
break;
case "*comment":
case "*c":
if (++i >= argsLength) return false;
comment = args[i];
break;
case "*?":
default:
error = true;
break;
}
}
return !error;
}
private static void Usage()
{
string example = "Usage: UpdateStatus -p \"Project Name\" -t \"Task name\" -h 3"
+ "\r\n\t\t[-r \"Resource Name\"] [-d 6/29/2011] [-c \"This is a comment\"]";
Console.WriteLine(example);
Console.WriteLine(" -projectName | -p: Name of the project.");
Console.WriteLine(" -taskName | -t: Name of the assigned task.");
Console.WriteLine(" -hours | -h: Number of hours worked.");
Console.WriteLine(" -resourceName | -r: Resource name. Default is the current user.");
Console.WriteLine(" -date | -d: Date of the work. Default is non-timephased.");
Console.WriteLine(" -comment | -c: Comment for submitted status.");
}
private static void ExitApp()
{
Console.Write("\nPress any key to exit... ");
Console.ReadKey(true);
Environment.Exit(0);
}
}
// Helper method: GetPSClientError.
class Helpers
{
/// <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;
}
}
}