All Windows-based and Web-based client applications for UMT Project Essentials use the Cost Module Service Interface (CMSI), a set of Web services built on the Microsoft .NET Framework 3.5 and the Windows Communication Foundation (WCF). The CMSI exposes the functionality and data that developers can use to extend Project Essentials and to integrate other applications with Project Essentials. Parameters of CMSI methods typically include DataSet objects.
The CMSI in Project Essentials has a dual interface. The Web service (ASMX) interface operates in the following way:
The Project Essentials Web extension client uses the WCF service interface for enhanced performance and security. Custom applications can use either interface; the ASMX and WCF interfaces have an identical object model for the public Web services. We recommend using the WCF interface for new applications.
The first thing you need to do is to generate the classes for the WCF proxy. This can be done in more ways. The first method would be by running the GenWCFProxyAssembly.cmd script available with this post. Before running the script you need to make a few modifications:
On line 19 you will have to input the server name and the Service Application GUID:
(set VDIR=http://ServerName:32843/8813747409d94599974b24602f230a0e/CMSI)
Replace the server name with the full computer (ie. project.contoso.umt.local). To get the Guid for the Service application you can either get it by running the IIS Manager:
Or you can open SharePoint 2010 management Shell and use Get-SPServiceApplication | Select ID,DisplayName
The GenWCFProxyAssembly.cmd script generates output files for the WCF services listed in the ClassList_WCF.txt, and then compiles the assembly. To run the script on a test installation of Project Essentials, do the following:
Note: The script uses svcutil.exe so instead of hardcoding the path you can run it from the Visual Studio Command Prompt
The other method to create the proxy classes is by using svcutil.exe to manually generate the proxy class for the desired WCF service. Here’s a sample of how to use it:
svcutil.exe /tcv:Version35 /serializer:XmlSerializer /nologo /t:code /l:CS http://project.contoso.umt.local:32843/41003eae452148dea374336005ddf3ad/CMSI/Granularity.svc?wsdl
Parameters used with this sample:
To use the generated classes you can either add a reference to the newly created ProjectEssentialsservices.dll or add the generated classes from the Source folder created by the script.
3.1.Configuring the WCF services programmatically
If it is used only for WCF configuration, exclude the app.config file provided.
To create a binding for the client endpoints, add the following code:
const int MAXSIZE = 500000000; // Set the final part of the URL address of the // front-end ProjectfinancialServer.svc router. const string svcRouter = "_vti_bin/PSI/ProjectFinancialServer.svc"; pwaUrl = pwaUri.Scheme + Uri.SchemeDelimiter + pwaUri.Host + ":" + pwaUri.Port + pwaUri.AbsolutePath; Console.WriteLine("URL: {0}", pwaUrl); // Create a basic binding that can be used for HTTP or HTTPS. BasicHttpBinding binding = null; if (pwaUri.Scheme.Equals(Uri.UriSchemeHttps)) { // Initialize the HTTPS binding. binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport); } else { // Initialize the HTTP binding. binding = new BasicHttpBinding( BasicHttpSecurityMode.TransportCredentialOnly); }
Set properties of the binding to enable use by the PSI-based application. The SendTimeout property should be a large enough time span to handle latency in accessing Project Server. The maximum message size property and the name table character count property should be large enough to handle very large datasets in Project Server. The MessageEncoding property must be set for text data, and the ClientCredentialType property must be set for NTLM credentials.
binding.Name = "basicHttpConf"; binding.SendTimeout = TimeSpan.MaxValue; binding.MaxReceivedMessageSize = MAXSIZE; binding.ReaderQuotas.MaxNameTableCharCount = MAXSIZE; binding.MessageEncoding = WSMessageEncoding.Text; binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
Create an endpoint address that points to the front-end ProjectFinancialServer.svc router in Project Web App. Finally, initialize each of the client variables with the WCF binding and endpoint address. The channel factory includes security and other properties of the underlying transport mechanism of the WCF service model.
// The endpoint address is the ProjectfinalcialServer.svc router for all public CMSI calls. EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter); SvcGranularity.GranularityClient granularityClient = new SvcGranularity.GranularityClient(binding, address); granularityClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation; granularityClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
3.2 Configuring the WCF services with app.config
In Visual Studio, on the Tools menu, click WCF Service Configuration Editor. In the Microsoft Service Configuration Editor application window, on the File menu, click Open, click Config File, and then navigate to the app.config file for the solution. When you directly set a service reference, Visual Studio creates two default bindings for each WCF service in your application. Visual Studio also creates two default endpoints for each service, and these endpoints use the default bindings. For example, the CustomBinding_Granularity endpoint uses the HTTPS protocol and the CustomBinding_Granularity1 endpoint uses the NET.TCP protocol.
In the Microsoft Service Configuration Editor window, in the Configuration pane, right-click Bindings, and then click New Binding Configuration. In the Create a New Binding dialog box, click basicHttpBinding, and then click OK. Rename the binding in the basicHttpBinding pane. For example, type basicHttpConf in the Name text box.
Change the general properties in the Binding tab, as listed below:
On the basicHttpBinding pane, click the Security tab, and then change the property values as listed below:
In the Configuration pane, expand the Advanced node, click Endpoint Behaviors, and then click New Endpoint Behavior Configuration. In the Behavior pane, change the Name field to basicHttpBehavior.
In the Configuration pane, under basicHttpBehavior, click the clientCredentials child node, and then change the AllowedImpersonationLevel to Impersonation. Leave the default values for the other properties (SupportInteractive = True, ImpersonationLevel= Identification, and AllowNtlm = True). Leave the child nodes under clientCredentials with the default values.
Create an endpoint that uses the basicHttpBinding, for the Granularity WCF service in the application:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/> </startup> <system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="basicHttpBehavior"> <clientCredentials> <windows allowedImpersonationLevel="Impersonation"/> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> <bindings> <basicHttpBinding> <binding name="BasicHttpBinding_Ntlm" 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> </basicHttpBinding> </bindings> <client> <endpoint address="http://ServerName/ProjectServerName/_vti_bin/cost/ProjectFinancialServer.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_Ntlm" behaviorConfiguration="basicHttpBehavior" contract="SvcGranularity.Granularity" name="basicHttp_Granularity" /> </client> </system.serviceModel> </configuration>
The following program will display the UID, start date, end date and the period name for the first opened actual for a project, using the app.config configuration. You need to add a reference to UMT.CostModule.Library in order to use have access to the PE classes.
Sample explained:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SvcGranularity; using UMT.CostModule.Library.Core; using UMT.CostModule.Library.Error; namespace UMT.ProjectEssentials.SDK.GetActualsForProject { class Program { static void Main(string[] args) { //set the UID for the project you want to read the actuals Guid projectGuid = new Guid("0898C874-B482-4EE2-A38E-C31C656C3473"); //instatiate a client for the Granularity Service GranularityClient granularityClient = new GranularityClient("basicHttp_Granularity"); //read the granularity periods for this project that are Actual Values SvcGranularity.TimePeriod[] periods = granularityClient.ReadGranularityPeriodsForProject(projectGuid, ValueTypes.ActualValue, true); //find what periods are opened GranularityInfo granInfo = granularityClient.ReadProjectActualsPeriods(projectGuid, periods.First<SvcGranularity.TimePeriod>().StartDate, periods.Last<SvcGranularity.TimePeriod>().StartDate, SvcGranularity.PeriodStatus.OPENED, true); if (granInfo.cmReportingPeriods.Rows.Count > 0) { GranularityInfo.cmReportingPeriodsRow firstPeriod = granInfo.cmReportingPeriods.Rows[0] as GranularityInfo.cmReportingPeriodsRow; //write the uid, start date,end date and period name for the first opened actual Console.WriteLine(String.Format("The first opened period : UID={0}; Start Date={1}; End Date={2}; Period Name={3}", firstPeriod.GranularityGUID, firstPeriod.StartDate, firstPeriod.EndDate, firstPeriod.PeriodName)); } //close the granularity service client when we're done granularityClient.Close(); Console.ReadLine(); } } }
You can download the files here:
What do you think?