Creating a PSI Extension for Project Server 2010

Applies to: Office 2010 | Project 2010 | Project Server 2010 | SharePoint Server 2010

In this article
Creating a Simple "Hello World" Service
Installing the PSI Extension
Testing the PSI Extension
Modifying the PSI Extension
Deploying and Testing the Modified Service
Debugging a PSI Extension
Best Practices for Project Server Extensions

A Project Server Interface (PSI) extension for Microsoft Project Server 2010 requires using the Windows Communication Foundation (WCF) and creating a service that is accessed through Project Web App. A service activation file is installed in the front-end web server extensions for Project Web App and the extension is configured by the web.config file in the same directory. (This article is adapted from content by Boaz Lev, Microsoft Corporation.)

The procedures in this article show how to:

  • Create a simple PSI extension named HelloService.

  • Deploy the extension by creating a service activation file (HelloService.svc) in Project Web App and modifying the web.config file in the Web Server Extensions directory for Project Server.

  • Create a service reference and test the simple extension.

  • Modify the extension to call the Resource service of the PSI, by programmatically configuring the ResourceClient object within the extension.

  • Return a ResourceDataSet from a method in the HelloService extension.

  • Use an App.config file to configure the HelloClient object in the test applications.

This article includes the following sections:

  • Creating a Simple "Hello World" Service

  • Installing the PSI Extension

  • Testing the PSI Extension

  • Modifying the PSI Extension

  • Deploying and Testing the Modified Service

  • Debugging a PSI Extension

  • Best Practices for Project Server Extensions

The Project 2010 SDK download includes the complete Visual Studio solutions for the basic HelloService project and the modified HelloService2 project.

Prerequisites

Developing WCF-based applications requires Microsoft Visual Studio 2008 Service Pack 1 or Visual Studio 2010. Applications must target the Microsoft .NET Framework 3.5. The procedures in this article use Microsoft Visual C#.

To more easily debug a PSI extension, development should be done on a test installation of Project Server 2010.

Creating a Simple "Hello World" Service

The simplest PSI extension includes an interface class that defines the signature of one method and a class that implements the interface. You can start a new Visual Studio project by using either the Class Library template in the Windows group of templates or the WCF Service Library template. With the Class Library template, you must add references and the main interface and implementation classes. With the WCF Service Library template, you can delete the App.config file and some code that the template creates. Procedure 1 uses the WCF Service Library template in Visual Studio 2010.

Procedure 1. To create a simple "Hello World" service

  1. In Visual Studio, create a project. In the New Project dialog box, select .NET Framework 3.5 in the top drop-down list. Click WCF in the Installed Templates pane, and then click WCF Service Library. For this example, name the project HelloService.

    The template creates the HelloService project, sets the namespace to HelloService, creates the IService1.cs file and the Service1.cs file, and adds references to System.Runtime.Serialization and System.ServiceModel for WCF.

  2. Delete the App.config file that the template generated. The PSI extension does not use an App.config file.

  3. Rename the IService1.cs file as IHello.cs, and then click Yes in the dialog box for renaming all references to IService1. Visual Studio renames the interface as IHello. Similarly, rename the Service1.cs file as Hello.cs. Visual Studio renames the class as Hello, which inherits from the IHello interface.

  4. To help demonstrate a feature of the service activation file in Procedure 2, rename the namespace. Select the HelloService namespace in the IHello.cs file, right-click the namespace, click Refactor, and then click Rename. For example, change the name to Microsoft.SDK.Project.Samples.HelloService (or another multiple-level name of your choosing). Click OK, click Apply, and then click Yes in the subsequent dialog boxes.

  5. Open the Properties pane of the HelloService project, and then click the Application tab. Verify that the Assembly name is HelloService, the Default namespace is Microsoft.SDK.Project.Samples.HelloService, and the Target framework is .NET Framework 3.5.

  6. Click the Signing tab in the Properties pane of the HelloService project, and select Sign the assembly. Create a strong name key file. For example, name the file Hello.snk, and then clear the check box for protecting the key file with a password. Click OK, and then close the project Properties pane.

  7. Open the IHello.cs file. Replace all of the code under the Microsoft.SDK.Project.Samples.HelloService namespace with the following code. The code defines the IHello interface for the service contract and the EchoHello method signature for the operation contract.

    {
        [ServiceContract]
        internal interface IHello
        {
            [OperationContract]
            string EchoHello();
        }
    }
    
  8. In the HelloService project, add a reference to System.Web. Open the Hello.cs file, and then add the following using statements.

    using System.ServiceModel.Activation;
    using System.Web;
    

    The System.ServiceModel.Activation namespace includes the AspNetCompatibilityRequirements class. A PSI extension service must be set as compatible with ASP.NET. For more information, see AspNetCompatibilityRequirementsMode Enumeration.

    The System.Web namespace includes the HttpContext class, from which you can get the URL of the service and other information.

    Note

    You can also get context for the WCF service from the System.ServiceModel.Channels.OperationContext object. However, in the statement, OperationContext context = OperationContext.Current, the value of context.Channel.LocalAddress.Uri does not include the part of the address that has the Project Web App instance name. For example, if the service URL is https://ServerName/pwa/_vti_bin/PSI/HelloService.svc, the Uri value is http://servername.domain.com/_vti_bin/PSI/HelloService.svc. SharePoint rewrites service URLs in the OperationContext object.

  9. Replace all of the code under the Microsoft.SDK.Project.Samples.HelloService namespace with the following code.

    {
        [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
        internal class Hello : IHello
        {
            private static HttpContext context;
    
            public Hello()
            {
                context = HttpContext.Current;
            }
    
            public string EchoHello()
            {
                string serviceUrl = GetServiceUri().ToString();
    
                return "Hello from the WCF service:\n" + serviceUrl;
            }
    
            #region Helpers
    
            // Get the URI of the service activation file.
            static Uri GetServiceUri()
            {
                var requestUri = context.Request.Url;
    
                int portNum = 80;
                var portString = requestUrl.GetComponents(UriComponents.Port, UriFormat.SafeUnescaped);
    
                if (!string.IsNullOrEmpty(portString))
                {
                    portNum = int.Parse(portString);
                }    
                var uriBuilder = new UriBuilder(
                    requestUrl.GetComponents(UriComponents.Scheme, UriFormat.SafeUnescaped),
                    requestUrl.GetComponents(UriComponents.Host, UriFormat.SafeUnescaped),
                    portNum,
                    context.Request.RawUrl);
    
                return uriBuilder.Uri;
            }
    
            #endregion
        }
    }
    

    The class access modifier is changed to internal, so that members are accessible only to other class methods in the same assembly. The EchoHello method must have the same signature that is defined in the IHello interface.

    The context variable is initialized in the Hello class constructor. The requestUri variable is initialized as type Uri.

    When you set the value of requestUri with the statement, requestUri = context.Request.Url;, the address value is missing the name of the Project Web App instance. By creating a UriBuilder object, it is possible to include the host name and the full path of the service. For example, if the service URL is https://localhost/pwa/_vti_bin/PSI/HelloService.svc, following are values of the UriBuilder constructor parameters:

    • scheme: http

    • host: localhost

    • port: When the port value is 80, it does not show in the URL.

    • pathValue: The RawUrl property value is /pwa/_vti_bin/PSI/HelloService.svc.

  10. Build the HelloService project.

Installing the PSI Extension

To install the HelloService.dll assembly, you must add it to the global assembly cache (GAC), create a service activation file, and then modify the web.config file for Project Web App.

Procedure 2. To install the "Hello World" service

  1. Open a Visual Studio Command Prompt window as administrator, and then change to the directory of the HelloService.dll assembly that you built in Procedure 1.

  2. To install the assembly in the GAC, run the following command:

    gacutil /if HelloService.dll
    
  3. Create a service activation (.svc) file for the PSI extension. The [Program Files]\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\PSI directory contains the front-end service files and the web.config file for all instances of Project Web App on the Project Server computer. For example, use a text editor to create a file named HelloService.svc, with the following contents (type the service attribute all on one line):

    <%@ServiceHost language="C#" 
        service="Microsoft.SDK.Project.Samples.HelloService.Hello, HelloService, 
        Version=1.0.0.0, Culture=neutral, PublicKeyToken=82a6a13f99b045eb" 
    %>
    

    Ensure that the service parameters match the values for your PSI extension, as follows:

    • The Microsoft.SDK.Project.Samples.HelloService.Hello value is the fully qualified class name of the PSI extension.

    • The HelloService value must match the name of the assembly for the service (HelloService.dll).

    • The Version, Culture, and PublicKeyToken values must match the values of the assembly in the GAC.

      To change the service parameter values

      1. Open the Windows\assembly directory in Windows Explorer. For example, on the Start menu, click Run, type assembly in the text box, and then click OK.

      2. Right-click the HelloService assembly name, and then click Properties.

      3. In the HelloService Properties dialog box, copy the Public Key Token value, and then paste the value in the HelloService.svc file for PublicKeyToken. If any other value is different, copy that value to the equivalent service parameter.

  4. Open the web.config file for Project Web App in a text editor, and then add a bindings element, a behaviors element, and a services element for the PSI extension. Each element is a child of the system.serviceModel element.

    To modify the web.config file

    1. Add the bindings element, with a basicHttpBinding that includes a binding name for the PSI extension and a binding name for the metadata exchange (MEX) endpoint, which enables you to create client applications.

          <bindings>
            <basicHttpBinding>
              <binding name="extensionBasicHttpConf"
                       closeTimeout="00:01:00"
                       openTimeout="00:01:00"
                       receiveTimeout="00:10:00"
                       sendTimeout="00:01:00"
                       allowCookies="true"
                       maxBufferSize="4194304"
                       maxReceivedMessageSize="500000000"
                       messageEncoding="Text"
                       transferMode="StreamedResponse">
                <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Ntlm" proxyCredentialType="Ntlm" realm="" />
                </security>
              </binding>
              <binding name="mexHttpBinding">
                <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Ntlm" proxyCredentialType="Ntlm" realm="" />
                </security>
              </binding>
            </basicHttpBinding>
          </bindings>
      

      The clientCredentialType attribute value and the proxyCredentialType attribute value must be Ntlm, not Windows. You can shorten the timeout values in a production environment.

    2. Add a behaviors element that enables the service to populate exception details into any fault that may occur while you are developing and testing the service. You can set the serviceDebug element to false in a production environment.

          <behaviors>
            <serviceBehaviors>
              <behavior name="PSIExtensionServiceBehavior">
                <serviceDebug includeExceptionDetailInFaults="true" />
                <serviceMetadata httpGetEnabled="true" />
              </behavior>
            </serviceBehaviors>
          </behaviors>
      
    3. Add a services element that includes a service child element for each PSI extension. Each service element contains an endpoint for the service contract and an endpoint for the MEX contract.

          <services>
            <service name="Microsoft.SDK.Project.Samples.HelloService.Hello"
                     behaviorConfiguration="PSIExtensionServiceBehavior">
              <endpoint address=""
                        binding="basicHttpBinding"
                        bindingConfiguration="extensionBasicHttpConf"
                        contract="Microsoft.SDK.Project.Samples.HelloService.IHello" />
              <endpoint address="mex"
                        binding="basicHttpBinding"
                        bindingConfiguration="mexHttpBinding"
                        name="mex"
                        contract="IMetadataExchange" />
            </service>
          </services>
      

      The name attribute must match the fully qualified name of the PSI extension class, as it is in the HelloWorld.svc activation file.

      The address attribute for the extension service is an empty string. The contract attribute must be the fully qualified interface class.

    4. Save the web.config file.

  5. In the Visual Studio Command Prompt window, run iisreset.

Testing the PSI Extension

You can use Internet Explorer to test the URL of the service activation file and determine whether the PSI extension is correctly installed and configured. If that works, then create a simple test application that uses the extension methods.

Procedure 3. To test the "Hello World" service

  1. In Internet Explorer, type the URL of the service activation file. For example, type https://localhost/pwa/_vti_bin/psi/helloservice.svc. If the PSI extension is installed correctly, Internet Explorer shows the Hello Service tab, as in Figure 1.

    Figure 1. Testing the PSI extension in Internet Explorer

    Testing the PSI extension in Internet Explorer

    If Internet Explorer shows an error, check the spelling of all the namespace instances and type names in the source code and configuration files, and then go through Procedure 2 again. If you recompile the HelloService.dll assembly, you can use the same gacutil command to overwrite the assembly in the GAC (gacutil /if HelloService.dll). Run iisreset again, and then refresh Internet Explorer.

  2. Create a simple test application that uses the EchoHello method in the HelloService extension.

    To create a simple test application

    1. In Visual Studio, create a solution that uses the Console Application template or the Windows Forms Application template. The steps in this procedure are for a Windows Forms Application template. For example, name the application TestHello.

      Note

      Create a separate Visual Studio solution for the test application. If you add a test project to the HelloService solution, you will not be able to debug the PSI extension through the test application.

    2. Add the following references:

      • System.Runtime.Serialization

      • System.ServiceModel

    3. Add a reference to the HelloService service. In Solution Explorer, right-click the TestHello project, and then click Add Service Reference. In the Add Service Reference dialog box, type the URL of the service. For example, type https://localhost/pwa/_vti_bin/psi/HelloService.svc, and then click Go. Type a namespace name; for example, type SvcHello (Figure 2), and then click OK.

      Figure 2. Adding a reference to the HelloService service

      Adding a service reference

    4. Add an App.config file to the project. Right-click the TestHello project, and then click Add New Item. In the Add New Item – TestHello dialog box, click the Application Configuration File template, name the file App.config, and then click Add.

    5. Open the App.config file, delete all the content, and then paste the following code into the file.

      <?xml version="1.0" encoding="utf-8" ?>
      <configuration>
        <system.serviceModel>
          <behaviors>
            <endpointBehaviors>
              <behavior name="basicHttpBehavior">
                <clientCredentials>
                  <windows allowedImpersonationLevel="Impersonation" />
                </clientCredentials>
              </behavior>
            </endpointBehaviors>
          </behaviors>
          <bindings>
            <basicHttpBinding>
              <binding name="basicHttpConf" sendTimeout="01:00:00" maxBufferSize="500000000"
                maxReceivedMessageSize="500000000">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                  maxBytesPerRead="4096" maxNameTableCharCount="500000000" />
                <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Ntlm" realm="" />
                </security>
              </binding>
              <binding name="BasicHttpBinding_IHello" closeTimeout="00:01:00"
                openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"
                useDefaultWebProxy="true">
                <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                  maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                <security mode="TransportCredentialOnly">
                  <transport clientCredentialType="Ntlm" proxyCredentialType="None"
                    realm="" />
                  <message clientCredentialType="UserName" algorithmSuite="Default" />
                </security>
              </binding>
            </basicHttpBinding>
          </bindings>
          <client>
            <endpoint address="https://ServerName/ProjectServerName/_vti_bin/PSI/HelloService.svc"
              behaviorConfiguration="basicHttpBehavior" binding="basicHttpBinding"
              bindingConfiguration="basicHttpConf" contract="SvcHello.IHello"
              name="basicHttp_HelloService" />
          </client>
        </system.serviceModel>
      </configuration>
      

      Change the address of the endpoint address to match your server name and Project Web App name. For more information about the App.config file for a WCF-based Project Server application, see Walkthrough: Developing PSI Applications Using WCF.

      Note

      The contract attribute of the endpoint element includes the namespace of the service reference that you added in step 2.c, and the type name of the interface.

  3. In Solution Explorer, delete the Form1.cs file. Open the Program.cs file, delete all the contents, and then paste in the following code.

    using System;
    using System.Windows.Forms;
    
    namespace TestHello
    {
        static class Program
        {
            [STAThread]
            static void Main()
            {
                SvcHello.HelloClient helloClient = null;
                string message = string.Empty;
    
                try
                {
                    helloClient = new SvcHello.HelloClient("basicHttp_HelloService");
    
                    message = helloClient.EchoHello();
                }
                catch (System.ServiceModel.FaultException ex)
                {
                    message =  "System.ServiceModel.FaultException:\n\n";
                    message += ex.Message + "\n\nInnerException:\n";
                    message += ex.InnerException;
                }
                catch (System.ServiceModel.CommunicationException ex)
                {
                    message = "System.ServiceModel.CommunicationException:\n\n";
                    message += "Source: " + ex.Source + "\n\n";
                    message += ex.Message;
                }
                finally
                {
                    // Close the client.
                    if (helloClient != null) helloClient.Close();
                }
                MessageBox.Show(message, "Test HelloService");
            }
        }
    }
    
  4. Press F5 to run the TestHello application.

The TestHello application calls the EchoHello method in the HelloClient object and displays a message box (Figure 3).

Figure 3. Using the HelloService in the TestHello application

Using the PSI extension in a simple application

Modifying the PSI Extension

You can modify the EchoHello method, change the signature of the method, or add new methods in the IHello interface and the Hello class of the HelloService project, and still use the same TestHello application. For example, Procedure 4 shows how to call a method in the Resource service of the PSI, within the HelloService extension.

When calling PSI methods within a PSI extension, you should programmatically configure the WCF bindings and endpoints of the PSI services instead of using an App.config file. To avoid having to install the ProjectServerServices.dll proxy assembly with the PSI extension, you should add the PSI proxy source files that are available in the Project 2010 SDK.

Procedure 4. To modify the service by adding a call to the PSI

  1. In Visual Studio, close the HelloService solution, and then copy the solution to another directory. For example, copy the solution to a directory that you create, named HelloService2.

    Do not change the file names, namespace, or type names in the HelloService2 directory. For the example in Procedure 4, the IHello interface remains the same.

  2. Open the HelloService solution in the HelloService2 directory. Add a reference to Microsoft.Office.Project.Server.Library, and then add the following using statements in the Hello.cs file.

    using System.ServiceModel.Activation;
    using PSLibrary = Microsoft.Office.Project.Server.Library;
    
  3. Add the wcf.Resource.cs proxy file for the Resource service from the Project 2010 SDK to the solution. The namespace in the proxy file is SvcResource.

  4. Add a class variable for the ResourceClient object:

    private static SvcResource.ResourceClient resourceClient;
    
  5. Add the SetClientEndpoint method within the Helpers region. The method initializes the resourceClient variable.

    /// <summary>
    /// Set the endpoint and initialize the resourceClient variable.
    /// </summary>
    /// <returns>The URL of the custom PSI service.</returns>
    private static string SetClientEndpoint()
    {
        // The maximum size constant must match the MAXSIZE value in the App.config 
        // file of the calling application.
        const int MAXSIZE = 500000000;
        const string PSI_SHARED = "_vti_bin/PSI/";
        const string ROUTER_SERVICE = "_vti_bin/PSI/ProjectServer.svc";
    
        Uri serviceUri = GetServiceUri();
    
        string serviceUrl = serviceUri.ToString();
    
        int indexOfPsi = serviceUrl.IndexOf(PSI_SHARED);
        string pwaUrl = serviceUrl.Remove(indexOfPsi);
        string routerService = pwaUrl + ROUTER_SERVICE;
    
        BasicHttpBinding binding = null;
    
        if (serviceUri.Scheme.Equals(Uri.UriSchemeHttps))
        {
            // Create binding for HTTPS.
            binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
        }
        else
        {
            // Create binding for HTTP.
            binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
        }
    
        binding.Name = "basicHttpConf";
        binding.AllowCookies = true;
        binding.MessageEncoding = WSMessageEncoding.Text;
    
        binding.OpenTimeout = TimeSpan.FromHours(1);
        binding.ReceiveTimeout = TimeSpan.FromHours(1);
        binding.SendTimeout = TimeSpan.FromHours(1);
    
        // If the TransferMode is buffered, the MaxBufferSize and 
        // MaxReceived MessageSize must be the same value.
        binding.TransferMode = TransferMode.Buffered;
        binding.MaxBufferSize = MAXSIZE;
        binding.MaxReceivedMessageSize = MAXSIZE;
        binding.ReaderQuotas.MaxArrayLength = MAXSIZE;
        binding.ReaderQuotas.MaxNameTableCharCount = MAXSIZE;
    
        binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
        binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Ntlm;
        binding.Security.Transport.Realm = "";
    
        // The endpoint address is the ProjectServer.svc router for all public PSI calls.
        EndpointAddress address = new EndpointAddress(routerService);
    
        resourceClient = new SvcResource.ResourceClient(binding, address);
        resourceClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel
            = TokenImpersonationLevel.Impersonation;
        resourceClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
    
        return serviceUrl;
    }
    

    In the HelloService extension, the serviceUrl value is, for example, https://localhost/pwa/_vti_bin/PSI/HelloService.svc. Because the modified PSI extension uses the Resource service, it must set the endpoint to the address of ProjectServer.svc, which acts as the router for calls to the PSI. The routerService value becomes https://localhost/pwa/_vti_bin/PSI/ProjectServer.svc.

    For more information about programmatically configuring a WCF-based Project Server application, see Walkthrough: Developing PSI Applications Using WCF.

  6. Modify the EchoHello method, as follows.

    public string EchoHello()
    {
        // Initialize the resourceClient variable.
        // The serviceUrl variable is used only for information in this example.
        string serviceUrl = SetClientEndpoint();
    
        Guid userUid = resourceClient.GetCurrentUserUid();
        SvcResource.ResourceDataSet dsResource = resourceClient.ReadResource(userUid);
    
        string userName = dsResource.Resources[0].RES_NAME;
        string userEmail = dsResource.Resources[0].WRES_EMAIL;
    
        string message = "Hello from the WCF service:\n" + serviceUrl;
        message += "\n\nThe following user called the service:\n";
        message += "\tName: " + userName + "\n\tEmail: " + userEmail;
    
        return message;
    }
    

    The sample code calls two methods in the Resource service; the ReadResource method requires the GUID of the resource, which is returned from GetCurrentUserUid. The sample code gets only the resource name and email properties of the current user from the Resources table of the ResourceDataSet.

  7. Compile the modified HelloService solution in the HelloService2 directory.

Deploying and Testing the Modified Service

Because the assembly name, namespace name, and interface name have not changed, you can use the same TestHello application to test the modified HelloService extension. Deployment simply requires installing the modified HelloServer.dll in the GAC, and then running iisreset.

Procedure 5. To deploy and test the modified service

  1. Open the Visual Studio Command Prompt, change to the HelloService2\bin\debug directory, and then run the following commands:

    gacutil /if HelloService.dll
    iisreset
    
  2. Refresh the service in Internet Explorer. The address of the PSI extension is the same as in Procedure 3: https://localhost/pwa/_vti_bin/psi/helloservice.svc. Internet Explorer should show the same tab as in Figure 1.

  3. In Visual Studio, open the TestHello solution. Because the namespace name, interface name, and EchoHello method signature have not changed, no changes to the TestHello application or configuration file are required.

    Important

    If you changed the EchoHello method signature in the IHello interface and the Hello class, or if you added methods, you must refresh the service reference. In Solution Explorer, right-click SvcHello in the Service References folder, and then click Update Service Reference.

  4. Press F5 to run the TestHello application.

The TestHello application calls the modified EchoHello method in the HelloClient object and displays a message box that includes data from the Resource service (Figure 4).

Figure 4. Using the modified HelloService in the TestHello application

Using the modified HelloService extension

Debugging a PSI Extension

To debug a PSI extension in Visual Studio, you must attach the extension project to an Internet Information Services (IIS) worker process named w3wp.exe. Attachment must occur after you have recompiled, deployed, and tested the service in Internet Explorer. When the project is attached to the correct w3wp process, the process is running in Visual Studio, and breakpoints are active. Procedure 6 shows how to find the correct w3wp process.

Procedure 6. To debug the modified HelloService service

  1. If you have made any changes in the HelloService project, recompile the project.

  2. Follow step 1 and step 2 in Procedure 5, to deploy the service. Refresh Internet Explorer to determine whether HelloService is active, and to reset the w3wp process that the service uses.

  3. Set a breakpoint in the Hello.cs file. For example, set a breakpoint on line 32 (string userName = dsResource.Resources[0].RES_NAME;).

  4. In Visual Studio, on the Debug menu, click Attach to Process. In the Attach to Process dialog box, select the Show processes from all users check box and the Show processes in all sessions check box. (Figure 5).

    Figure 5. Attaching to a w3wp process for debugging

    Attaching to a w3wp process for debugging

  5. Select a w3wp process, and then click Apply. If there is more than one w3wp process, you can use one of the following methods to find the correct process:

    • Attach to all of the w3wp processes.

    • Choose one process, and then click Apply. If that is not the correct process, the breakpoint shows as a disabled icon and the tooltip shows that the breakpoint cannot be hit (Figure 6). Stop debugging (press Shift + F5), and then attach to a different w3wp process.

      Figure 6. Attaching to an incorrect w3wp process disables the breakpoint

      An incorrect process disables the breakpoint

    • If none of the previous methods work, recompile and redeploy the HelloService extension, run iisreset, and then refresh HelloService in Internet Explorer. Try the previous methods again, until the breakpoint icon remains solid red.

  6. In a separate instance of Visual Studio, open the TestHello solution, and then start debugging (press F5).

When the instance of Visual Studio that is running HelloService is attached to the correct w3wp process, debugging from the TestHello instance stops at the solid red breakpoint in the HelloService instance.

Best Practices for Project Server Extensions

The following are best practice guidelines for developing PSI extensions for Project Server 2010:

  • Do not use the PSI extension to directly modify any Project Server database objects (tables, views, stored procedures, and so on).

  • To read and manipulate Project Server data, use built-in PSI methods within the extension.

  • You can read project Server data from the RDB, or create tables and views for additional purposes in the RDB.

  • Incorporate security into your PSI extensions. Do not return data to users who should not have access to it.

  • PSI methods can throw run-time exceptions in many situations. Ensure that all your code is protected with try/catch blocks so that the service can return sensible reply messages to the client application.

See Also

Tasks

Walkthrough: Developing PSI Applications Using WCF

Other Resources

Developing PSI Extensions

Walkthrough: Creating a PSI Extension for Project Server 2007

AspNetCompatibilityRequirementsMode Enumeration

Blog Article: How to Make PSI Extensions in Project Server 2010—Part I

Blog Article: How to Make PSI Extensions in Project Server 2010—Part II