Getting Started with the PSI

Many people have asked, “How do I get started working with the PSI?” So I figured I would blog about creating a very simple application that interacts with the PSI. For this example, I will create a simple Windows Application that connects to Project Server and retrieves a list of resources for a given project.

Before we begin, it is important to realize that the PSI is made up of a number of Web Services. You can find a list of all the Project Server Web Service here: https://msdn2.microsoft.com/en-us/library/ms488627.aspx These Web Services are logically separated by business objects. For this example, we will be using both the Project and Resource Web Service.

To get started, open visual studio and create a Windows Application. The first step will be to add web references to the Project and Resource Web Services:

1. In the Solution Explorer, right click on References

2. Click on Add Web Reference.

3. Type in the URL to the Project Web Service.

The URL for the web service is:

https://SERVER_NAME/PWA_INSTANCE/_vti_bin/psi/project.asmx

Where SERVER_NAME is the name of the server Project Server is hosted on and PWA_INSTANCE is the name of the Project Web Access instance you want to connect to. _vti_bin/psi is where all the Project Server PSI Web Services reside. project.asmx is specific to the Project Web Service.

4. Give the Web Reference a name, such as WSProject

5. Click Add Reference

This will add a reference to the Project Web Service. Repeat the same steps again, except this time, on step 3 specify resource.asmx instead of project.asmx and in step for name the Web Reference WSResource.

Now that the Web References are step up, we can start to program! When I develop against the PSI, I always create a connection object to handle the various connections to the PSI. This allows me to reuse the connection class in a number of applications. Below is the source code of my connection class:

using System;

using System.Collections.Generic;

using System.Text;

using System.Data;

using System.Net;

using System.Resources;

using System.Globalization;

using System.Web.Services.Protocols;

using System.Reflection;

namespace PSIDemo

{

    public class Connection

    {

        public const string Resource = "Resource";

        public const string Project = "Project";

        private static Dictionary<string, SoapHttpClientProtocol> WSDictionary;

        private string ms_ProjServURL;

        public Connection(string as_ProjServURL)

        {

            ms_ProjServURL = as_ProjServURL + "/_vti_bin/psi/";

            WSDictionary = new Dictionary<string, SoapHttpClientProtocol>();

        }

        public SoapHttpClientProtocol GetWebService(string as_WSName)

        {

            SoapHttpClientProtocol lo_WS;

            if (WSDictionary.TryGetValue(as_WSName.ToString(), out lo_WS) == false)

            {

                switch(as_WSName)

                {

                    case Resource:

                        Auth(Resource, new WSResource.Resource());

                        break;

                    case Project:

                        Auth(Project, new WSProject.Project());

                        break;

              }

                lo_WS = WSDictionary[as_WSName];

            }

            return lo_WS;

        }

        public static void Reset()

        {

            WSDictionary.Clear();

        }

        private void Auth(string as_WSName, SoapHttpClientProtocol as_WS)

        {

            try

            {

                object [] parameters = new object [1];

                parameters[0] = ms_ProjServURL + as_WSName + ".asmx";

                MethodInfo setUrlMethod = as_WS.GetType().GetProperty("Url").GetSetMethod();

                setUrlMethod.Invoke(as_WS, parameters);

                parameters[0] = CredentialCache.DefaultCredentials;

                MethodInfo setCredentialsMethod = as_WS.GetType().GetProperty("Credentials").GetSetMethod();

                setCredentialsMethod.Invoke(as_WS, parameters);

                WSDictionary.Add(as_WSName, as_WS);

            }

            catch (Exception ex)

            {

                throw ex;

            }

        }

    }

}

 

This connection class has a dictionary of all the available PSI Web Services. With this implementation we are only concerned with the Project and Resource Web Service, so I have not included any other web services, but it would not be difficult to add additional PSI Web Services. It would only require a couple more lines of code for each Web Service. I will save that for another post.

The method that handles the setup to the Web Service is Auth. For each Web Service, we need to set the URL for the server that we want to connect to at run time. The URL was passed in with the constructor and this can be a different URL then the one used for the Web Reference. The second step is to set the credentials. For this example, we will only do NT authentication, but if there is interest, I can post an extension for Forms Authentication. Once that is done, we add the Web Service to the dictionary and it is ready to be used.

Next, I am going to add three controls, plus a few labels to the Windows form. The first control is a text box for the URL to the Project Server (txtURL), the second control is a drop down which will be populated with all the Projects the user has access to (cboProjects) and the third control will be a list box which will contain the names of resources that belong to the selected project (lstResources). Below is a screen shot of the form:

This form has two methods that contain all the calls to the Web Services. The first method is the Leave event for the URL textbox:

 

 

private void txtURL_Leave(object sender, EventArgs e)

{

cboProjects.Items.Clear();

      conn = new Connection(txtURL.Text);

      projWS = (WSProject.Project)conn.GetWebService(Connection.Project);

      DataTable projList = projWS.ReadProjectList().Tables[0];

      foreach (DataRow dr in projList.Rows)

      {

      cboProjects.Items.Add(new ProjListItem(dr["Proj_Name"].ToString(), new Guid(dr[0].ToString())));

  }

      if (cboProjects.Items.Count > 0)

      {

      cboProjects.SelectedItem = cboProjects.Items[0];

      }

}

In this method, we instantiate the Connection object and pass in the URL for the Project Server. This is the URL that will be used at run time. Next, we get the Project and Resource Web Services from the connection object. This allows us to read the projects and populate the drop down with all the project names. I have created a basic object, ProjListItem, which contains the GUID and name of the project so that we can easily retrieve the GUID later on to get the list of resources. It is important to note here, that we are working with datasets. The majority of our Web Services have datasets that can be manipulated and sent back to the server to update the data.

The second method is the select index changed for the drop down list of project names:

 

 

private void cboProjects_SelectedIndexChanged(object sender, EventArgs e)

{

lstResources.Items.Clear();

           

WSProject.ProjectTeamDataSet pds;

ProjListItem projItem = (ProjListItem)cboProjects.SelectedItem;

pds = projWS.ReadProjectTeam(projItem.getGuid());

DataTable dt = pds.Tables["ProjectTeam"];

foreach (DataRow dr in dt.Rows)

      {

      lstResources.Items.Add(dr["Res_Name"].ToString());

}

}

 

Here we retrieve the GUID for the selected project from the ProjListItem object that we populated the drop down list in the first method and we get the resources on the team by calling ReadProjectTeam method and passing the selected project GUID. ReadProjectTeam returns a dataset that contains a data table “ProjectTeam” that lists all the resources that are team members on the project.

So, now we have a little application that is able to connect to the server and retrieve data,

Chris Boyd