次の方法で共有


WCF を使用して PSI アプリケーションを開発する

適用対象: Office 2010 | Project 2010 | Project Server 2010 | SharePoint Server 2010

この記事の内容
WCF 用のコンソール アプリケーションを作成する
サービス参照を追加する
PIS でのプログラミング
Project Server Queuing Service の使用
完全なコード例

Microsoft Project Server 2010 では、Project Server Interface (PSI) の WCF サービスと ASMX Web サービスの両方のインターフェイスへのアクセスに Windows Communication Foundation (WCF) を使用しています。この記事では、Project Server 用の WCF サービスを開発する方法と、Project Server Queuing Service の使用方法を説明します。WCF のバインドとエンドポイントを完全にコードで作成することもできますが、WCF サービス構成エディターでアプリケーションの app.config ファイルに変更を加えることもできます。app.config ファイルを使用すると、アプリケーションを再コンパイルせずに再構成することができます。たとえば、トランスポート プロトコルを HTTP から直接 TCP/IP に変更できます (この記事のサンプル コードは、Microsoft Corporation の Tiger Wang が作成したテスト アプリケーションを出典としています)。

この記事は次のセクションで構成されます。

  • WCF 用のコンソール アプリケーションを作成する

  • サービス参照を追加する

    • サービスをプログラムで構成する

    • サービスを app.config で構成する

  • PIS でのプログラミング

  • Project Server Queuing Service の使用

  • 完全なコード例

WCF を使用した Project Server アプリケーションの開発に関する概念の概要については、「WCF と PSI の概要」を参照してください。PSI に対する IntelliSense の説明のセットアップに関するヒントやサービス参照を追加する 3 つの方法に関する詳細を含めて、開発で使用する一般的な手順については、「WCF ベースのコード サンプルの前提条件」を参照してください。「Beginner's Guide to Windows Communication Foundation」も参照してください。

Project 2010 SDK ダウンロードには、WCFHelloProject アプリケーション用の 2 つの Microsoft Visual Studio プロジェクトが含まれています。

  • ダウンロード内の WCFHelloProject_Prog ディレクトリは、Microsoft Visual Studio 2008 SP1 によるエンドポイントのプログラム的な構成のためのプロジェクトです。このソリューションでは app.config ファイルを使用しません。Project サービスおよび QueueSystem サービスへの参照は、Visual Studio 内で直接設定されます。

    注意

    WCF ベースのコード サンプルの前提条件」で説明したように、Project Server 2010 のベータ後のビルドでサービス参照を直接設定するには、Project Server サービス アプリケーションの web.config ファイルを一時的に置き換える必要があります。PSI サービスのプロキシ アセンブリを使用するか、アプリケーションで使用するサービスごとにプロキシ ソース コード ファイルを追加することをお勧めします。プロキシ ソース コード ファイルは、プロキシ アセンブリを作成するスクリプトを実行したときに作成されます。Project 2010 SDK には、プロキシ ソース コード ファイルが含まれています。

  • WCFHelloProject_vs10_CfgEd ディレクトリでは、Microsoft Visual Studio 2010 と app.config ファイルを使用します。Project サービスおよび QueueSystem サービスへの参照では、PSI プロキシ アセンブリを作成するスクリプト内の SvcUtil.exe コマンドで作成される wcf.Project.cs および wcfQueueSystem.cs プロキシ ソース コード ファイルを使用します。

前提条件

WCF アプリケーションの開発には、Microsoft Visual Studio 2008 SP1 または Visual Studio 2010 が必要です。アプリケーションは Microsoft .NET Framework 3.5 をターゲットにする必要があります。この記事の手順では、Microsoft Visual C# を使用しています。

Project Server アプリケーションの場合、Microsoft SharePoint Server 2010 用のクライアント サンドボックス ソリューションも含めるのでない限り, .NET Framework 3.5 Client Profile ではなく .NET Framework 3.5 をターゲットにします。SharePoint クライアント開発の詳細については、SharePoint Foundation 2010 SDK 内の「What's New: Client Object Model」を参照してください。

この手順では、Project Server コンピューターのテスト インストール上で実行している Visual Studio を使用します。

注意

Project Server アプリケーションの開発は、Project Server の運用インストール上ではなく、テスト インストール上で行ってください。

注意

アプリケーションを別のコンピューター上で実行する場合は、アプリケーションと共に Microsoft.Office.Project.Server.Library.dll をインストールする必要があります。Project Server 2010 SDK ダウンロードには、Microsoft.Office.Project.Server.Library.dll アセンブリを再配布するためのライセンスが含まれています。

クレーム ベース認証   SharePoint 2010 プラットフォーム上に構築されたアプリケーションに対する認証はすべてクレーム ベースです。WCFHelloProject の例は、Windows 認証のみを使用する Project Server インストール用に設計されています。Project Server が複数認証 (Windows 認証とフォーム ベース認証の両方) 用に構成されている場合、PSI メソッドのすべての呼び出しを OperationContextScope セクション内に入れる必要があります。これにより、フォーム ベース認証をブロックするヘッダーが送信 Web 要求に追加されます。詳細については、「WCF ベースのコード サンプルの前提条件」を参照してください。

WCF 用のコンソール アプリケーションを作成する

WCFHelloProject コンソール アプリケーションは、単純な Project Server アプリケーションのテストに再利用できるサンプルです。Main メソッド、ParseCommandLine メソッド、および Usage メソッドのコードは、さまざまなアプリケーションに応用することができます。

手順 1. PSI 用の WCF コンソール アプリケーションを作成するには

  1. 管理者として Visual Studio を実行し、コンソール アプリケーションを作成します。コンソール アプリケーションの名前は、たとえば WCFHelloProject にします。[新しいプロジェクト] ダイアログ ボックスの上部にあるドロップダウン リストで、ターゲット フレームワークとして [.NET Framework 3.5] を選択します。

  2. 次の参照を追加します。

    • System.Runtime.Serialization は、サービス参照を設定したときに生成される WCF サービス プロキシ コード (Reference.cs) の中で使用されます。

    • System.ServiceModel は、WCF サービスを構成するために必要です。Web アプリケーションの場合は、System.ServiceModel.Web も必要になります。ASMX Web サービスを追加する場合は、System.Web.Services が必要です。

    • Microsoft.Office.Project.Server.Library は、[Program Files]\Microsoft Office Servers\14.0\Bin\Microsoft.Office.Project.Server.Library.dll アセンブリ内にあります。アプリケーションで Project Server イベントを使用する場合は、同じディレクトリ内の Microsoft.Office.Project.Server.Events.Receivers.dll アセンブリへの参照も追加します。

  3. Program.cs ファイル内で次の Program クラス変数を追加します。最後の 2 つの変数は WCFHelloProject アプリケーションに固有のものです。

    static private Uri pwaUri;          // URI of Project Web App.
    static private string pwaUrl;       // URL of Project Web App.
    static private string argError;     // Contains the argument that is in error.
    
    static private int numProjects;     // Number of projects to create.
    static private bool deleteProjects; // Specifies whether to delete projects.
    
  4. ParseCommandLine メソッドを作成します。このメソッドはコマンド ラインを解析し、引数エラーがあれば argError を設定します。Program.cs ファイル内のすべてのメソッドのサンプル コードについては、「完全なコード例」を参照してください。

  5. Usage メソッドを作成します。このメソッドはコマンドの使用法を表示します。

  6. Main メソッド内でクラス変数を初期化します。手順 2. で CreateProjects クラスを作成した後、ParseCommandLine メソッドで true が返されたときに実行されるコードを追加します。

    static void Main(string[] args)
    {
        pwaUrl = string.Empty;
        pwaUri = null;
        numProjects = 2;
        deleteProjects = true;
        argError = null;
    
        if (args.Length == 0)
        {
            Usage(argError);
        }
        else if (ParseCommandLine(args))
        {
            // TODO: Add code to instantiate the CreateProjects class and run its methods.
        }
        else
        {
            Usage(argError);
        }
        // Keep the Command Prompt window open for debugging.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey(true);
    }
    

アプリケーションの残りの部分のクラスを Program.cs ファイルに追加することもできますが、コードの再利用のためには、アプリケーションのメイン部分について別々のファイルを作成する方が簡単です。

サービス参照を追加する

PSI の WCF インターフェイスを使用するには、サービス参照を追加して構成する必要があります。手順 2. では、サービス参照を追加する方法を説明します。手順 3. では、サービスをプログラムで構成する方法を説明します。手順 4. に従って、app.config ファイルでサービスを構成することもできます。

サービス参照に付ける名前空間名は自由に決められますが、WCF サービス参照および ASMX Web サービス参照の命名規則に従って名前を付けるのが望ましいでしょう。たとえば、Project 2010 SDK では、一般に WCF サービスに対して Svc[ServiceName] という形式を使用しています (たとえば、SvcProject)。PSI プロキシ アセンブリのための SDK ダウンロード内の Visual Studio Intellisense ファイル (ProjectServerServices.xml) でも、Svc プレフィックスを使用しています。

ヒント

この記事では、Visual Studio 内でサービス参照を直接追加する方法を説明します。この方法よりも、Project 2010 SDK ダウンロードに含まれている ProjectServerServices.dll プロキシ アセンブリを使用し、このアセンブリへの参照を設定することをお勧めします。「WCF ベースのコード サンプルの前提条件」には、サービス参照を作成するための詳細な手順が記載されています。また、プロキシ アセンブリやプロキシのソース コード ファイルを使用するための手順も記載されています。ProjectServerServices.dll プロキシ アセンブリへの参照を設定することの利点の 1 つは、同じディレクトリ内の ProjectServerServices.xml ファイルを使用して、Intellisense で型とメンバーの説明が表示されるようにすることができる点です。

手順 2. WCF サービス参照を追加するには

  1. [方法] WCF サービスのプロキシ アセンブリを作成する」で説明しているように、バックエンド Project サービス仮想ディレクトリ内の web.config ファイルを一時的に置き換えます。iisreset を実行します。

  2. Visual Studio のソリューション エクスプローラーで、[参照設定] ノードを右クリックし、[サービス参照の追加] をクリックします。[サービス参照の追加] ダイアログ ボックスの [アドレス] フィールドに、必要とする PSI サービスの URL を入力するか貼り付けます。Project サービスの場合、次のものを貼り付けます。

    https://localhost:32843/[GUID]/PSI/Project.svc
    

    ただし、GUID は SharePoint Web サービス アプリケーション内の Project Server サービス仮想ディレクトリの名前です ([インターネット インフォメーション サービス (IIS) マネージャー] を参照してください)。仮想ディレクトリ名は SharePoint 内での Project Server Service アプリケーションの ID でもあります。サービス URL の詳細については、「WCF と PSI の概要」内の WCF インターフェイスと ASMX インターフェイスの使い方に関するセクションを参照してください。

    ヒント

    Project Server Service アプリケーションの GUID をすばやく調べるには、SharePoint Server 2010 と共にインストールされる Windows PowerShell のコマンドを使用します。[スタート] メニューで [すべてのプログラム]、[Microsoft SharePoint 2010 製品]、[SharePoint 2010 管理シェル] の順にクリックします。次に示すのはコマンドとその結果です (実際の GUID とは異なります)。

    PS> $serviceName = "Project Server Service Application"

    PS> get-SPServiceApplication -name $serviceName | select Name, Id

    Name                                    Id

    Project Server Service Application      51125047-6279-4ae8-890a-b67a5daec75e

    ディレクトリ名は 5112504762794ae8890ab67a5daec75e です (ダッシュは含まれません)。

  3. Project サービス名前空間の名前を入力します。たとえば、「SvcProject」と入力してから [OK] をクリックします (図 1)。

    図 1. WCF サービス参照の追加

    WCF サービス参照の追加

    サービス参照を追加すると、Visual Studio によって、そのサービスの既定の 2 つのバインドとエンドポイントが含まれる app.config ファイルも追加されます。app.config ファイルを WCF にのみ使用する場合は、このファイルを除外または削除し、サービスをプログラムで構成することができます (手順 3.)。あるいは、WCF サービス構成エディターを使用して、app.config ファイルを編集することもできます (手順 4.)。

  4. 同様に、QueueSystem サービスへの参照を追加し、その名前を SvcQueueSystem とします。

  5. サービスを構成する前に、Visual Studio で WCFHelloProject プロジェクトにメイン アプリケーション コードのクラス ファイルを追加します。たとえば、[新しい項目の追加 ? WCFHelloProject] ダイアログ ボックスで、[クラス] をクリックし、ファイル名を CreateProjects.cs にします。Visual Studio によって CreateProjects クラスが追加されます。

  6. 次の using ステートメントを CreateProjects.cs ファイル内の既定のステートメントに追加します。backendProject 名前空間エイリアスと backendQueueSystem 名前空間エイリアスはオプションです。これらは WCFHelloProject 例の中での注意喚起のためのもので、Project サービスおよび QueueSystem サービスの参照がローカル (バックエンド) Project Server サービス用のものであって、外部のコンピューターからは直接アクセスできないことを表しています。

    using System.Net;
    using System.Security.Principal; // Required for the TokenImpersonationLevel enumeration.
    using System.ServiceModel;
    
    using PSLibrary = Microsoft.Office.Project.Server.Library;
    using backendProject = WCFHelloProject.SvcProject;
    using backendQueueSystem = WCFHelloProject.SvcQueueSystem;
    

    Project サービスおよび QueueSystem サービスに対する直接参照を設定せずに、PSI プロキシ アセンブリを使用するかプロキシ ソース コード ファイルを追加する場合、これらのサービス名前空間は WCFHelloProject 名前空間の子ではありません。たとえば、次のステートメントは、プロキシ ソース ファイル内で名前空間のエイリアスを設定します。

    using backendProject = SvcProject;          // The wcfProject.cs file contains the SvcProject namespace.
    using backendQueueSystem = SvcQueueSystem;  // The wcf.QueueSystem.cs file contains the SvcQueueSystem namespace.
    
  7. コンストラクターで初期化されるデータのクラス変数を追加して、そのデータにクラス内のすべてのメソッドからアクセスできるようにします。手順 3. または手順 4. で、projectClient 変数と queueSystemClient 変数を、Project Web App 内でのフロントエンド (パブリック) ProjectServer.svc ルーターのエンドポイント アドレスに設定して、バックエンド PSI サービス参照にアクセスできるようにします。

    private static backendProject.ProjectClient projectClient;
    private static backendQueueSystem.QueueSystemClient queueSystemClient;
    private static string pwaUrl;
    private static int numProjects;
    private static bool deleteProjects;
    

    Intellisense で ProjectClient や backendProject 変数 (バックエンド Project サービスを指し示す) 内の他のインターフェイスとクラスが表示されることに注意してください。

    注意

    ProjectClient などのクライアント クラスは、WCF インターフェイスだけに存在します。Project Web サービスへのフロントエンド ASMX 参照を追加するか、SDK ダウンロード内の GenASMXProxyAssembly.cmd スクリプトで作成される ProjectServerServices.dll アセンブリへの参照を設定するか、wsdl.Project.cs などのプロキシ ソース コード ファイルを追加した場合、ProjectClient クラスは存在しません。

  8. CreateProjects クラスを初期化するためのコンストラクターと、サービス参照を構成するためのメソッドを追加します。CreateProjects クラス コンストラクターでは、Project Web App URI に対して Uri パラメーターを使用して、URL のすべての部分にプログラム的にアクセスできるようにします。num パラメーターは作成するプロジェクトの数を表し、delete パラメーターはプロジェクトの作成後にそれらを削除するかどうかを表します。

    public CreateProjects(Uri uri, int num, bool delete)
    {
        numProjects = num;
        deleteProjects = delete;
    
        SetClientEndpoints(uri);
    }
    
    private static void SetClientEndpoints(Uri pwaUri)
    {
    }
    
  9. サービス参照の設定が終わったら、「[方法] WCF サービスのプロキシ アセンブリを作成する」で説明したように、バックエンド Project サービス仮想ディレクトリ内の元の web.config ファイルを置き換えます。iisreset を実行します。

手順 3. または手順 4. を使用すると、SetClientEndpoints メソッドにコードを追加し、サービスを構成することができます。

サービスをプログラムで構成する

WCF サービスをプログラムで構成することの利点の 1 つは、WCF の使い方を習得する際に、SetClientEndpoints メソッドへのコード例の貼り付けが一般にすばやく簡単に行えることです。プログラムで構成する方法は、Project Web App リボンを変更するアプリケーションを作成するときにも使用できます。その場合は、「チュートリアル : PWA リボンのカスタマイズと JS グリッドのアクセス」で説明されているように、app.config ファイルを変更しないでください。プログラムでの構成のもう 1 つの特徴は (それが利点かどうかはアプリケーションの要件によりますが)、アプリケーションの修正と再コンパイルを行わない限り変更できないことです。

トランスポート メカニズム、セキュリティ設定、タイムアウトなど、アプリケーションの設定を、再コンパイルをせずに変更または追加する必要がある場合は、app.config ファイルを使用する方が便利です (手順 4.)。

手順 3. WCF サービスをプログラムで構成するには

  1. PSI サービスへの参照を設定したときに Visual Studio によって作成された app.config ファイルを WCF の構成にだけ使用する場合は、そのファイルを除外します。

  2. クライアント エンドポイントのバインドを作成するには、次のコードを SetClientEndpoints メソッドに追加します。

     const int MAXSIZE = 500000000;
    
    // Set the final part of the URL address of the 
    // front-end ProjectServer.svc router.
    const string svcRouter = "_vti_bin/PSI/ProjectServer.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);
    }
    
  3. バインドのプロパティを設定して、PSI ベースのアプリケーションで使用できるようにします。SendTimeout プロパティは、Project Server へのアクセスでの遅延を処理するのに十分な時間に設定する必要があります。最大メッセージ サイズを表すプロパティと名前テーブルの文字数を表すプロパティは、Project Server 内の最大のデータセットを処理するのに十分な大きさに設定する必要があります。MessageEncoding プロパティは、テキスト データ用に設定する必要があります。ClientCredentialType プロパティは、NTLM 資格情報用に設定する必要があります。

    binding.Name = "basicHttpConf";
    binding.SendTimeout = TimeSpan.MaxValue;
    binding.MaxReceivedMessageSize = MAXSIZE;
    binding.ReaderQuotas.MaxNameTableCharCount = MAXSIZE;
    binding.MessageEncoding = WSMessageEncoding.Text;
    binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
    
  4. Project Web App 内のフロントエンド ProjectServer.svc ルーターを指し示すエンドポイント アドレスを作成します。最後に、各クライアント変数を WCF のバインドおよびエンドポイント アドレスで初期化します。チャネル ファクトリには、WCF サービス モデルのトランスポート メカニズムのセキュリティやその他のプロパティが含まれます。

    // The endpoint address is the ProjectServer.svc router for all public PSI calls.
    EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter);
    
    projectClient = new backendProject.ProjectClient(binding, address);
    projectClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel
        = TokenImpersonationLevel.Impersonation;
    projectClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
    
    queueSystemClient = new backendQueueSystem.QueueSystemClient(binding, address);
    queueSystemClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel
        = TokenImpersonationLevel.Impersonation;
    queueSystemClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
    
  5. Program.Main メソッドにコードを追加して、CreateProjects オブジェクトを初期化します。Create メソッドおよび DisposeClients メソッドの呼び出しは、これらのメソッドを書くまでコメント アウトされた状態にしておきます。

    . . .
    else if (ParseCommandLine(args))
    {
        // The command is valid, so instantiate the CreateProjects object.
        CreateProjects projectCreator = 
            new CreateProjects(pwaUri, numProjects, deleteProjects);
    
        //projectCreator.Create();
        //projectCreator.DisposeClients();
    }
    . . .
    
  6. アプリケーションをテストして、CreateProjects オブジェクトが正しく初期化されるかどうかを確認します。

    1. [WCFHelloProject のプロパティ] ウィンドウで [デバッグ] タブを選択し、[コマンドライン引数] ボックスに -pwaUrl https://localhost/pwa というコードを追加します (Project Web App インスタンスの名前が pwa であるものとします)。

    2. SetClientEndpoints メソッドの右中かっこ ("}") にブレークポイントを設定してからアプリケーションを実行します。

    3. いくつかの WCF 設定の値を確認します。たとえば、projectClient.ChannelFactory の値を確認します。これは {System.ServiceModel.ChannelFactory<WCFHelloProject.SvcProject.Project>} でなければなりません。

    4. projectClient.Endpoint の値を確認します。これは Address={https://localhost/pwa/_vti_bin/PSI/ProjectServer.svc} でなければなりません。

    5. アプリケーションの終わりに達すると、[コマンド プロンプト] ウィンドウに次のように表示されます。

      URL: https://localhost:80/pwa/
      Press any key to exit.
      

サービスを app.config で構成する

app.config ファイルまたは web.config ファイルを使用して WCF サービスを構成することの利点の 1 つは、アプリケーションを再コンパイルせずにエンドポイントのバインドを変更できることです。たとえば、トランスポートを HTTP から TCP に変更できます。もう 1 つの利点は、WCF サービス構成エディターを使用してバインドとエンドポイントの追加や変更ができることです。app.config や WCF サービス構成エディターを使用することの欠点は、Project Server でバインドが正しく機能するように設定するための項目が数多くあることです。

Visual Studio が app.config ファイル内に作成する既定のバインドとエンドポイントは、Project Server では機能しません。手順 4. では、これらの既定設定を修正するのではなく、新しいバインドとエンドポイントを作成するための方法を説明します。

手順 4. WCF サービスを app.config で構成するには

  1. Visual Studio で WCFHelloProject ソリューションを閉じ、WCFHelloProject ディレクトリを別の場所にコピーし、その新しい場所の WCFHelloProject.sln ファイルを開きます。

  2. app.config ファイルをプロジェクトから除外した場合は、それを元に戻します。ソリューション エクスプローラーで [WCFHelloProject] を右クリックし、[追加] をクリックし、[既存の項目] をクリックします。それからプロジェクトの新しい場所にある app.config ファイルに移動します。

    手順 3. で、app.config ファイルをプロジェクトから除外するだけではなく削除した場合は、新しい app.config ファイルを作成します。Project 2010 SDK ダウンロード内の ProjectServerServices.dll プロキシ アセンブリまたは wcf.Project.cs ファイルと wcf.QueueSystem.cs ファイルを使用した場合も、app.config ファイルを作成する必要があります。app.config ファイルの作成方法の詳細については、「WCF ベースのコード サンプルの前提条件」の「サービス構成ファイルを追加する」を参照してください。

  3. Visual Studio の [ツール] メニューで、[WCF サービス構成エディター] をクリックします。[Microsoft Service Configuration Editor] アプリケーション ウィンドウの [ファイル] メニューで、[開く] をクリックし、[構成ファイル] をクリックします。それから、そのソリューションの app.config ファイルに移動します。

    ヒント

    app.config ファイルを初めて開いた後、ソリューション エクスプローラーでこのファイルを右クリックし、[WCF 構成の編集] をクリックすることができます。

    [Microsoft Service Configuration Editor] で、"認識できない属性 'decompressionEnabled' …" などのエラーが表示された場合は、[OK] をクリックしてから、Visual Studio で app.config ファイルを編集して、そのサポートされていない属性をすべての場所から削除します。app.config を保存してから [Microsoft Service Configuration Editor] を再起動します。

    サービス参照を直接設定すると、Visual Studio によってアプリケーションの WCF サービスごとに既定の 2 つのバインドが作成されます (図 2)。また、サービスごとに既定の 2 つのエンドポイントも作成されます。これらのエンドポイントでは既定のバインドが使用されます。たとえば、[CustomBinding_Project] エンドポイントでは HTTPS プロトコルが使用され、[CustomBinding_Project1] エンドポイントでは NET.TCP プロトコルが使用されます。

    図 2. app.config 内の既定のエンドポイントとバインド (Visual Studio 2010 と .NET Framework 3.5 を使用)

    app.config の既定のエンドポイントとバインド

  4. [Microsoft Service Configuration Editor] ウィンドウの [構成] ウィンドウで、[バインド] を右クリックし、[新しいバインドの構成] をクリックします。[新しいバインドの作成] ダイアログ ボックスで、[basicHttpBinding] をクリックし、[OK] をクリックします。[basicHttpBinding] ウィンドウでバインドの名前を変更します。たとえば、[名前] ボックスに「basicHttpConf」と入力します。

    表 1 に示すように、[バインド] タブで一般的なプロパティを変更します。

    表 1. バインド プロパティ

    プロパティ

    コメント

    MaxBufferSize

    500000000

    MaxReceivedMessageSize

    500000000

    SendTimeout

    10675199.02:48:05.4775807 が有効な最大値です。01:00:00 で試してください。

    この大きな値は TimeSpan.MaxValue に相当します。これは 10675199 日 2 時間 48 分と約 5 秒で、29,000 年を超える長さです。この値をより現実的な値、たとえば 01:00:00 (1 時間) に引き下げることができます。

    MaxArrayLength

    16384

    MaxBytesPerRead

    4096

    MaxDepth

    32

    MaxNameTableCharCount

    500000000

    MaxStringContentLength

    8192

  5. [basicHttpBinding] ウィンドウで、[セキュリティ] タブをクリックし、プロパティ値を表 2 のように変更します。

    表 2. セキュリティ プロパティ

    プロパティ

    コメント

    Mode

    TransportCredentialOnly

    TransportCredentialOnly モードでは、メッセージの暗号化や署名を行わずにユーザー資格情報を送信します。Realm URI に HTTPS プロトコルを使用する場合は、Mode 属性を Transport に設定します。詳細については、「Programming WCF Security」を参照してください。

    Realm

    [http://<セキュリティ ドメイン>]

    セキュリティ ドメイン (https://microsoft.com など)。既定値は空の文字列です。詳細については、「Claims-Based Architectures」を参照してください。

    TransportClientCredentialType

    Ntlm

    セキュリティ バインドについては、「Transport Security Overview」を参照してください。

  6. [構成] ウィンドウで [詳細設定] ノードをクリックします。さらに、[エンドポイントの動作] をクリックし、[新しいエンドポイント動作の構成] をクリックします。[動作] ウィンドウで、[名前] フィールドを「basicHttpBehavior」に変更します。

  7. [動作: basicHttpBehavior] ウィンドウで [追加] をクリックします。[動作要素拡張セクションの追加] ダイアログ ボックスで、[clientCredentials] をクリックし、[追加] をクリックします。

  8. [構成] ウィンドウで、[basicHttpBehavior] の下にある [clientCredentials] 子ノードをクリックし、[AllowedImpersonationLevel] を [偽装] に変更します。その他のプロパティは既定値のままにしておきます ([SupportInteractive] = True、[ImpersonationLevel] = 識別、[AllowNtlm] = True)。[clientCredentials] の下の子供ノードは既定値のままにしておきます。

  9. アプリケーションの WCF サービスごとに、[basicHttpBinding] を使用するエンドポイントを作成します。

    手順 4a. クライアント エンドポイントを作成するには

    1. [構成] ウィンドウで、[エンドポイント] を右クリックし、[新しいクライアント エンドポイント] をクリックします。[クライアント エンドポイント] ウィンドウで、エンドポイントの名前を入力します。たとえば、Project サービスとして「basicHttp_Project」を入力します。

    2. [アドレス] フィールドに、Project Web App 内の ProjectServer.svc ルーターの URL を入力します。たとえば、「https://ServerName/ProjectServerName/_vti_bin/PSI/ProjectServer.svc」と入力します。

    3. [BehaviorConfiguration] フィールドのドロップダウン リストで、前に作成したカスタム エンドポイント動作の名前を選択します。たとえば、[basicHttpBehavior] を選択します。

    4. [バインド] フィールドで、バインドの種類を選択します。この場合は、[basicHttpBinding] を選択します。

    5. [BehaviorConfiguration] フィールドで、前に作成したバインドを選択します。この場合は、[basicHttpConf] を選択します。

    6. [コントラクト] フィールドをクリックし、WCF コントラクトが含まれている実行ファイルを参照するボタンをクリックします。[コントラクト型ブラウザー] ダイアログ ボックスで、WCFHelloProject ディレクトリの \bin\Debug サブディレクトリ内の実行ファイルに移動します。そして WCFHelloProject.exe ファイルをクリックし、[開く] をクリックします。[コントラクト型ブラウザー] に実行ファイル内のサービス クラスが表示されます (図 3)。SvcProject.Project をクリックし、[開く] をクリックします。

      注意

      Visual Studio 2010 は、エラー ダイアログ ボックスに "ファイルまたはアセンブリ … が読み込めませんでした" というメッセージを表示します。名前空間とサービス クラスの名前を入力することによって、[コントラクト] フィールドでサービスの種類を直接設定することもできます。たとえば、「SvcProject.Project」と入力します。

      Visual Studio オブジェクト ブラウザーで WCF コントラクトのインターフェイス名を確認することもできます。[オブジェクト ブラウザー] タブで、WCFHelloProject ノードを展開します。たとえば、SvcProject 名前空間には Project インターフェイスが含まれています。

      図 3. コントラクト型ブラウザー (Visual Studio 2008)

      コントラクト型ブラウザーの使用

    7. 同じ方法で QueueSystem サービスのエンドポイントを作成します。たとえば、エンドポイントの名前を basicHttp_QueueSystem とし、[コントラクト] フィールドを SvcQueueSystem.QueueSystem に設定します。このエンドポイントの他のフィールドは、Project エンドポイントのものと同じです。

    8. [Microsoft Service Configuration Editor] の [ファイル] メニューで [上書き保存] をクリックし、エディターを閉じます。

  10. app.config ファイル内の behaviors、bindings、endpoints の各要素の内容を確認します。

    <behaviors>
      <endpointBehaviors>
        <behavior name="basicHttpBehavior">
          <clientCredentials>
            <windows allowedImpersonationLevel="Impersonation" />
          </clientCredentials>
        </behavior>
        <!-- (More behaviors.) -->
      </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="http//SecurityDomain" />
          </security>
        </binding>
      </basicHttpBinding>
      <!-- (More bindings.) -->
    </bindings>
    . . .
    <client>
      <endpoint address="https://ServerName/ProjectServerName/_vti_bin/PSI/ProjectServer.svc"
          behaviorConfiguration="basicHttpBehavior" binding="basicHttpBinding"
          bindingConfiguration="basicHttpConf" contract="SvcProject.Project"
          name="basicHttp_Project" kind="" endpointConfiguration="" />
      <endpoint address="https://ServerName/ProjectServerName/_vti_bin/PSI/ProjectServer.svc"
          behaviorConfiguration="basicHttpBehavior" binding="basicHttpBinding"
          bindingConfiguration="basicHttpConf" contract="SvcQueueSystem.QueueSystem"
          name="basicHttp_QueueSystem" />
      <!-- (More client endpoints.)-->
    </client>
    

    ヒント

    WCF サービスの 1 つのグループに関して動作とバインドとエンドポイントのセットをいったん作成すれば、app.config ファイル内のそれらのセクションをテンプレートとして使用することにより、[Microsoft Service Configuration Editor] による手順を使用せずに同じようなセットを作成できます。

  11. CreateProjects.cs ファイルで、projectClient オブジェクトと queueSystemClient オブジェクトを、app.config で指定したエンドポイント名で初期化します。

    注意

    SetClientEndpoints メソッドには pwaUri パラメーターは必要ありません。WCF の設定は app.config に含まれており、手順 3. のようにプログラムで設定するわけではないからです。

    private static void SetClientEndpoints()
    {
        projectClient = new backendProject.ProjectClient("basicHttp_Project");
        queueSystemClient = new backendQueueSystem.QueueSystemClient("basicHttp_QueueSystem");
    }
    
  12. 手順 3. のステップ 6. のように、デバッグ用のコマンド ライン引数を設定します。SetClientEndpoints メソッドにブレークポイントを設定し、ステートメントをステップ実行し、いくつかの WCF 設定の値 (projectClient.ChannelFactory プロパティ値や projectClient.Endpoint プロパティ値) を確認します。

アプリケーションが実行され、クライアント エンドポイントが正しく設定されていれば、projectClient オブジェクトと queueSystemClient オブジェクトを使用して、PSI の Project サービスと QueueSystem サービスのメソッドを呼び出すことができます。

PIS でのプログラミング

手順 3. (プログラムでの構成) または手順 4. (app.config での構成) を使用して、projectClient オブジェクトと queueSystemClient オブジェクトを、Project Web App 内のパブリック ProjectServer.svc ルーターを経由するエンドポイントで初期化できます。手順 5. では、PSI 呼び出しを使用してプロジェクトの作成と削除を行う 2 つのメソッドを追加する方法を説明します。

手順 5. Create メソッドを開発するには

  1. PSI 呼び出しで必要なデータを準備します。CreateProjects クラスの Create メソッドは、作成するプロジェクトの数だけ QueueCreateProject メソッドを反復します。QueueCreateProject のパラメーターは、jobUid (Project Server キュー ジョブ GUID)、dataset (作成するプロジェクトごとに ProjectRow を 1 つずつ持つ ProjectDataSet オブジェクト)、および validateOnly (プロジェクトを作成せずにデータの検証のみを行うかどうかを指定する) です。

    projDs オブジェクトは、backendProject 名前空間エイリアス内の ProjectDataSet 型定義に基づいてインスタンス化されます (SvcProject サービス参照内の reference.cs ファイル内に型定義があります)。projDs オブジェクト内の ProjectDataTable の NewProjectRow メソッドによって、ProjectRow 型の projRow オブジェクトを作成する必要があります。なぜなら、このプロジェクト データ行は、その ProjectDataSet オブジェクトに属しているからです。

    プロジェクトを作成するには、少なくとも、プロジェクト型、プロジェクト GUID、プロジェクト名という 3 つのプロパティが必要です。これらのプロパティがプロジェクト行内に設定された後、AddProjectRow メソッドがその行を projDs オブジェクトに追加します。

    public void Create()
    {
        // Prepare the data.
        Int32 projectType = Convert.ToInt32(PSLibrary.Project.ProjectType.Project);
        Guid[] projectUids = new Guid[numProjects];
        backendProject.ProjectDataSet[] projDs = new backendProject.ProjectDataSet[numProjects];
    
        for (int i = 0; i < numProjects; i++)
        {
            projectUids[i] = Guid.NewGuid();
            projDs[i] = new backendProject.ProjectDataSet();
            backendProject.ProjectDataSet.ProjectRow projRow = projDs[i].Project.NewProjectRow();
    
            projRow.PROJ_TYPE = projectType;
            projRow.PROJ_UID = projectUids[i];
            projRow.PROJ_NAME = "WCFTEST_" + projectUids[i].ToString();
    
            projDs[i].Project.AddProjectRow(projRow);
        }
        . . .
    }
    
  2. ProjectDataSet ごとに projectClient オブジェクト (つまり、パブリック ProjectServer.svc サービスに対するエンドポイントを持つオブジェクト) の QueueCreateProject メソッドを呼び出します。次のコードのコンソール出力は、WCFHelloProject テスト アプリケーションで指定された数の呼び出しに要する時間を示すためのものです。

    // Create the projects.
    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine(string.Format("Creating {0} projects via WCF.",
                                    numProjects.ToString()));
    Console.ResetColor();
    
    DateTime startTime = DateTime.Now;
    
    for (int i = 0; i < numProjects; i++)
    {
        projectClient.QueueCreateProject((projectUids[i], projDs[i], false);
    }
    
    Console.ForegroundColor = ConsoleColor.Green;
    Console.WriteLine("Time: {0} ms",
        (new TimeSpan((DateTime.Now.Ticks - startTime.Ticks))).TotalMilliseconds);
    Console.ResetColor();
    
  3. Create メソッドの残りの行については、次のコードで Helpers.WaitForQueue メソッドを使用しています。このメソッドは Project Server Queuing Service がプロジェクトを作成し終わるのを待ちます (手順 6. を参照)。CreateProjects.Delete については、次のステップで説明します。

    Helpers.WaitForQueue(backendQueueSystem.QueueMsgType.ProjectCreate,
                         numProjects, queueSystemClient, startTime);
    
    if (deleteProjects) Delete(projectUids);
    
  4. CreateProjects クラス内に Delete メソッドを作成します。Delete は、削除しているプロジェクトの数に関する情報をコンソールに書き込み、QueueDeleteProjects メソッドを呼び出し、Project Server がプロジェクトの削除に要したミリ秒数を書き込みます。

    public void Delete(Guid[] projUids)
    {
        Console.Write("Deleting {0} project(s)...", numProjects);
    
        DateTime startTime = DateTime.Now;
        Guid jobUid = Guid.NewGuid();
    
        projectClient.QueueDeleteProjects(jobUid, true, projUids, true);
    
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("\nTime: {0} ms",
            (new TimeSpan((DateTime.Now.Ticks - startTime.Ticks))).TotalMilliseconds);
        Console.ResetColor();
    
        Helpers.WaitForQueue(backendQueueSystem.QueueMsgType.ProjectDelete,
                             numProjects, queueSystemClient, startTime);
    }
    

アプリケーションのテストを試みる前に、Helpers クラスと WaitForQueue メソッドを作成します。

Project Server Queuing Service の使用

QueueSystem サービス内の ReadMyJobStatus メソッドを使用すると、アプリケーションは Project Server Queuing Service 内の指定したジョブの種類の状態を読み取り、それらのジョブが完了するのを待つことができます。キューを待たずに WCFHelloProject アプリケーションを実行した場合、このアプリケーションはすべてのプロジェクトが作成される前にそれらの削除を試み、例外を発生させます。

手順 6. QueueSystem サービスを使用するには

  1. QueueSystem サービスを使用するためのユーティリティ メソッドを備えたクラスを WCFHelloProject アプリケーションに追加します。たとえば、CreateProjects.cs ファイル内に、WaitForQueue メソッドを持つ Helpers クラスを追加します。WaitForQueue は、QueueSystem サービス内の ReadMyJobStatus メソッドを呼び出します。このメソッドには、確認するジョブの種類、ジョブの数、および開始時刻を指定するパラメーターがあります。ReadMyJobStatus を呼び出すためには、queueSystemClient オブジェクトがフロントエンド ProjectServer.svc ルーター経由で QueueSystem サービスにアクセスする必要があります。

    class Helpers
    {
        public static bool WaitForQueue(backendQueueSystem.QueueMsgType jobType, int numJobs,
                                        backendQueueSystem.QueueSystemClient queueSystemClient,
                                        DateTime startTime)
        {
            . . .
        }
    }
    
  2. WaitForQueue メソッド内に次のコードを追加します。maxSeconds2Wait 定数を使用することにより、問題がある場合や Project Server Queuing Service が長くかかりすぎる場合に、アプリケーションが窮地を脱することができます。ReadMyJobStatus メソッドは、queueStatusDs オブジェクト内の QueueStatusDataSet を返します。

    処理されたジョブの数が総数より小さく、経過時間が指定の最大値より小さい限り、ReadMyJobStatus を 1 秒 (1000 ミリ秒) ごとに呼び出します。QueuePosition 列に基づき、最後に使用された並べ替え順序で、queueStatusDs 内の結果を並べ替えます。

    キューのすべてのジョブが処理されるか、待ち時間が最大値を超えると、WaitForQueue は true を返します。

    const int maxSeconds2Wait = 50;
    backendQueueSystem.QueueStatusDataSet queueStatusDs = new backendQueueSystem.QueueStatusDataSet();
    
    int timeout = 0;    // Number of seconds waited.
    Console.Write("Waiting for job " + jobType.ToString());
    
    backendQueueSystem.QueueMsgType[] messageTypes = { jobType };
    backendQueueSystem.JobState[] jobStates = { backendQueueSystem.JobState.Success };
    
    while ((timeout < maxSeconds2Wait) && (queueStatusDs.Status.Count < numJobs))
    {
        System.Threading.Thread.Sleep(1000);
    
        queueStatusDs = queueSystemClient.ReadMyJobStatus(
            messageTypes,
            jobStates,
            startTime,
            DateTime.Now,
            numJobs,
            true,
            backendQueueSystem.SortColumn.QueuePosition,
            backendQueueSystem.SortOrder.LastOrder);
    
        timeout++;
        Console.Write(".");
    }
    Console.WriteLine();
    
    if (queueStatusDs.Status.Count == numJobs)
    {
        return true;
    }
    return false;
    
  3. アプリケーションを実行してテストするために、Program.Main クラス内の次の 2 行をコメント解除します。

    projectCreator.Create();
    projectCreator.DisposeClients();
    
  4. **デバッグ:**DataSet の内容をより簡単に確認するには、DataSet ビジュアライザー ツールを使用します。たとえば、WaitForQueue メソッド内の if (queStatusDS.Status.Count == numJobs) 行にブレークポイントを設定します。アプリケーションがブレークポイントに達したら、マウス ポインターを queueStatusDs の上に移動し、ポップアップ デバッグ値の中の小さな虫眼鏡アイコンをクリックします。図 4 は [DataSet ビジュアライザー] ダイアログ ボックスを示しています。DataSet のいずれかのテーブル内の行のプロパティをすべて目視検査したり、それらのデータ行をテキスト エディターにコピーしたりすることができます。QueueStatusDataSet には Status テーブルのみが含まれます。WCFHelloProject の既定の実行では 2 つのプロジェクトが作成されるので、Project Server Queuing Service はアプリケーション ユーザーのために MessageType = 22 の 2 つのキュー ジョブのみを実行します。Microsoft.Office.Project.Server.Library 名前空間内の QueueConstants.QueueMessageType 列挙 (QueueConstants.QueueMsgType) は、メッセージの種類の値 22 が ProjectCreate であることを示しています。

    図 4. DataSet ビジュアライザーによるデバッグ

    DataSet Visualizer の使用

WCFHelloProject アプリケーションを立て続けに何回も実行すると、一般にキュー ジョブ時間が減少していき、それからおよそ 3 ~ 4 回実行すると、その後は値があまり変化しなくなります。たとえば、次の出力は 5 回の実行の結果を示すものですが、これを見ると初回の実行から時間が大幅に低下していることがわかります。その理由は、Project Server のキュー システムがキュー ジョブの種類と頻度を内部的に記録していて、類似したジョブが立て続けに入ってきた場合に、それらのジョブをより迅速に処理できるようにキューを調整しているからです。そのため、Project Server はタイムシート提出などの類似したジョブが立て続けに入ってきたときに効率よく処理できるのです。QueueSystem サービスの詳細については、「[方法] QueueSystem サービスを使用する」を参照してください。

URL: https://localhost:80/pwa/
Creating 2 projects via WCF.
Time: 57004.164 ms
Waiting for job ProjectCreate.......
Deleting 2 project(s)...
Time: 852.4845 ms
Waiting for job ProjectDelete.
_________________________________
URL: https://localhost:80/pwa/
Creating 2 projects via WCF.
Time: 3779.055 ms
Waiting for job ProjectCreate.
Deleting 2 project(s)...
Time: 1267.497 ms
Waiting for job ProjectDelete.
_________________________________
URL: https://localhost:80/pwa/
Creating 2 projects via WCF.
Time: 2144.394 ms
Waiting for job ProjectCreate.
Deleting 2 project(s)...
Time: 350.5635 ms
Waiting for job ProjectDelete.
_________________________________
URL: https://localhost:80/pwa/
Creating 2 projects via WCF.
Time: 1719.6165 ms
Waiting for job ProjectCreate.
Deleting 2 project(s)...
Time: 335.916 ms
Waiting for job ProjectDelete..
_________________________________
URL: https://localhost:80/pwa/
Creating 2 projects via WCF.
Time: 2259.621 ms
Waiting for job ProjectCreate.
Deleting 2 project(s)...
Time: 361.305 ms
Waiting for job ProjectDelete.

WCFHelloProject アプリケーションは WCF 経由での PSI の呼び出しを使用して、指定された数のプロジェクトを Project Server の下書きデータベース内に作成し、それからプロジェクトの作成に要した時間を表示し、さらに削除に要した時間を表示します。PSI クライアント オブジェクトのエンドポイントの設定は、プログラムでの構成によって、あるいは app.config と Visual Studio の [Microsoft Service Configuration Editor] を使用することによって行えます。図 5 は、実行中のアプリケーションのコンソール ウィンドウのスクリーンショットです。

図 5. WCFHelloProject.exe のコンソール ウィンドウ出力

WCFHelloProject のコンソール ウィンドウの出力

完全なコード例

以下は Program.cs ファイル内の完全なコードコードです。Program クラス内の ParseCommandLine メソッドはコマンド ライン引数を検証します。Main メソッドは CreateProjects オブジェクトをインスタンス化します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WCFHelloProject
{
    class Program
    {
        static private Uri pwaUri;          // URI of Project Web App.
        static private string pwaUrl;       // URL of Project Web App.
        static private string argError;     // Contains the argument that is in error.

        static private int numProjects;     // Number of projects to create.
        static private bool deleteProjects; // Specifies whether to delete projects.

        static void Main(string[] args)
        {
            pwaUrl = string.Empty;
            pwaUri = null;
            numProjects = 2;
            deleteProjects = true;
            argError = null;

            if (args.Length == 0)
            {
                Usage(argError);
            }
            else if (ParseCommandLine(args))
            {
                // The command is valid, so instantiate the CreateProjects object.
                CreateProjects projectCreator = 
                    new CreateProjects(pwaUri, numProjects, deleteProjects);

                projectCreator.Create();
                projectCreator.DisposeClients();
            }
            else
            {
                Usage(argError);
            }
            // Keep the Command Prompt window open for debugging.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey(true);
        }

        // Parse the command line.
        static private bool ParseCommandLine(string[] args)
        {
            const int MAXNUM = 20;
            bool error = false;
            bool argErr = false;

            int argsLength = args.Length;

            for (int i = 0; i < args.Length; ++i)
            {
                if (error) break;

                switch (args[i].ToLower())
                {
                    case "/pwaurl":
                    case "-pwaurl":
                        i++;
                        if (i >= argsLength) return false;
                        pwaUrl = args[i];

                        if (pwaUrl.ToLower().StartsWith("http"))
                        {
                            // Add a trailing slash, if it is not present.
                            if (pwaUrl.LastIndexOf("/") != pwaUrl.Length - 1)
                                pwaUrl += "/";

                            // Convert to a URI, for easier access to properties.
                            pwaUri = new Uri(pwaUrl);
                        }
                        else
                            argErr = true;
                        break;

                    case "/numprojects":
                    case "-numprojects":
                        i++;
                        if (i >= argsLength) return false;
                        numProjects = Convert.ToInt32(args[i]);

                        if (numProjects < 0 || numProjects > MAXNUM) argErr = true;
                        break;

                    case "/delete":
                    case "-delete":
                        i++;
                        if (i >= argsLength) return false;

                        string theArg = args[i].ToLower();

                        switch (theArg)
                        {
                            case "t":
                            case "true":
                            case "1":
                                deleteProjects = true;
                                break;
                            case "f":
                            case "false":
                            case "0":
                                deleteProjects = false;
                                break;
                            default:
                                argErr = true;
                                break;
                        }
                        break;

                    case "/?":
                    case "-?":
                        error = true;
                        break;

                    default:
                        argError = args[i];
                        error = true;
                        break;
                }
            }
            if (pwaUrl == string.Empty) error = true;
            if (argErr) error = true;

            return !error;
        }

        // Show the command usage.
        static private void Usage(String errorInfo)
        {
            if (errorInfo != null)
            {
                // A command-line argument error occurred. Report it to the user.
                Console.WriteLine("Error: {0} is not an argument.\n", errorInfo);
            }

            Console.WriteLine(string.Format(
                "Usage:  [/?] -{0} {1} [-{2} {3}] [-{4} {5}]",
                "pwaUrl", @"""<URL>""",
                "numProjects", "<0 - 20>",
                "delete", "<T | F>"));

            Console.WriteLine(
                "\n\tpwaUrl:\t\tExample: https://ServerName/pwa"
                + "\n\n\tnumProjects:\tOptional. Number of projects to create."
                + "\n\t\t\tThe default is 2; maximum is 20."
                + "\n\n\tdelete:\t\tOptional. Delete projects after creating them."
                + "\n\t\t\tThe default is true.");
        }   
    }
}

以下は CreateProjects.cs ファイル内の CreateProjects クラスと Helpers クラスの完全なコードです。SetClientEndpoints メソッドは、クライアント エンドポイントをプログラム的に設定します (app.config は使用しません)。app.config を使用するコード例については、Project 2010 SDK ダウンロード内の WCFHelloProject_vs10_CfgEd ディレクトリを参照してください。

using System;
using System.Net;
using System.ServiceModel;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PSLibrary = Microsoft.Office.Project.Server.Library;
using backendProject = WCFHelloProject.SvcProject;
using backendQueueSystem = WCFHelloProject.SvcQueueSystem;

namespace WCFHelloProject
{
    class CreateProjects
    {
        private static backendProject.ProjectClient projectClient;
        private static backendQueueSystem.QueueSystemClient queueSystemClient;
        private static string pwaUrl;
        private static int numProjects;
        private static bool deleteProjects;

        public CreateProjects(Uri uri, int num, bool delete)
        {
            numProjects = num;
            deleteProjects = delete;
 
           SetClientEndpoints(uri);
        }

        private static void SetClientEndpoints(Uri pwaUri)
        {
            const int MAXSIZE = 500000000;
            const string svcRouter = "_vti_bin/PSI/ProjectServer.svc";

            pwaUrl = pwaUri.Scheme + Uri.SchemeDelimiter + pwaUri.Host + ":"
                + pwaUri.Port + pwaUri.AbsolutePath;
            Console.WriteLine("URL: {0}", pwaUrl);

            // Create a binding for HTTP.

            BasicHttpBinding binding = null;

            if (pwaUri.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.SendTimeout = TimeSpan.MaxValue;
            Console.WriteLine("SendTimeout value:\n\t{0} days,\n\t{1} hours,\n\t{2} minutes,\n\t{3} seconds",
                binding.SendTimeout.Days.ToString(), binding.SendTimeout.Hours.ToString(),
                binding.SendTimeout.Minutes.ToString(), binding.SendTimeout.Seconds.ToString());

            binding.MaxReceivedMessageSize = MAXSIZE;
            binding.ReaderQuotas.MaxNameTableCharCount = MAXSIZE;
            binding.MessageEncoding = WSMessageEncoding.Text;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;

            // The endpoint address is the ProjectServer.svc router for all public PSI calls.
            EndpointAddress address = new EndpointAddress(pwaUrl + svcRouter);

            projectClient = new backendProject.ProjectClient(binding, address);
            projectClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel
                = TokenImpersonationLevel.Impersonation;
            projectClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;

            queueSystemClient = new backendQueueSystem.QueueSystemClient(binding, address);
            queueSystemClient.ChannelFactory.Credentials.Windows.AllowedImpersonationLevel
                = TokenImpersonationLevel.Impersonation;
            queueSystemClient.ChannelFactory.Credentials.Windows.AllowNtlm = true;
        }

        public void DisposeClients()
        {
            projectClient.Close();
            queueSystemClient.Close();
        }

        public void Create()
        {
            // Prepare the data.
            Int32 projectType = Convert.ToInt32(PSLibrary.Project.ProjectType.Project);
            Guid[] projectUids = new Guid[numProjects];
            backendProject.ProjectDataSet[] projDs = new backendProject.ProjectDataSet[numProjects];

            for (int i = 0; i < numProjects; i++)
            {
                projectUids[i] = Guid.NewGuid();
                projDs[i] = new backendProject.ProjectDataSet();
                backendProject.ProjectDataSet.ProjectRow projRow = projDs[i].Project.NewProjectRow();

                projRow.PROJ_TYPE = projectType;
                projRow.PROJ_UID = projectUids[i];
                projRow.PROJ_NAME = "WCFTEST_" + projectUids[i].ToString();

                projDs[i].Project.AddProjectRow(projRow);
            }

            // Create the projects.
            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine(string.Format("Creating {0} projects via WCF.",
                                            numProjects.ToString()));
            Console.ResetColor();

            DateTime startTime = DateTime.Now;

            for (int i = 0; i < numProjects; i++)
            {
                projectClient.QueueCreateProject(projectUids[i], projDs[i], false);
            }

            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Time: {0} ms",
                (new TimeSpan((DateTime.Now.Ticks - startTime.Ticks))).TotalMilliseconds);
            Console.ResetColor();

            Helpers.WaitForQueue(backendQueueSystem.QueueMsgType.ProjectCreate,
                                 numProjects, queueSystemClient, startTime);

            if (deleteProjects) Delete(projectUids);
        }

        public void Delete(Guid[] projUids)
        {
            Console.Write("Deleting {0} project(s)...", numProjects);

            DateTime startTime = DateTime.Now;
            Guid jobUid = Guid.NewGuid();

            projectClient.QueueDeleteProjects(jobUid, true, projUids, true);

            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("\nTime: {0} ms",
                (new TimeSpan((DateTime.Now.Ticks - startTime.Ticks))).TotalMilliseconds);
            Console.ResetColor();

            Helpers.WaitForQueue(backendQueueSystem.QueueMsgType.ProjectDelete,
                                 numProjects, queueSystemClient, startTime);
        }
    }

    class Helpers
    {
        public static bool WaitForQueue(backendQueueSystem.QueueMsgType jobType, int numJobs,
                                        backendQueueSystem.QueueSystemClient queueSystemClient,
                                        DateTime startTime)
        {
          const int maxSeconds2Wait = 50;
          backendQueueSystem.QueueStatusDataSet queueStatusDs = new backendQueueSystem.QueueStatusDataSet();

            int timeout = 0;    // Number of seconds waited.
            Console.Write("Waiting for job " + jobType.ToString());

            backendQueueSystem.QueueMsgType[] messageTypes = { jobType };
            backendQueueSystem.JobState[] jobStates = { backendQueueSystem.JobState.Success };

            while ((timeout < maxSeconds2Wait) && (queueStatusDs.Status.Count < numJobs))
            {
                System.Threading.Thread.Sleep(1000);

                queueStatusDs = queueSystemClient.ReadMyJobStatus(
                    messageTypes,
                    jobStates,
                    startTime,
                    DateTime.Now,
                    numJobs,
                    true,
                    backendQueueSystem.SortColumn.QueuePosition,
                    backendQueueSystem.SortOrder.LastOrder);

                timeout++;
                Console.Write(".");
            }
            Console.WriteLine();

            if (queueStatusDs.Status.Count == numJobs)
            {
                return true;
            }
            return false;
        }
    }
}

次の手順

WCFHelloProject 例の中のコードには、Try … Catch ステートメントが含まれていません。これは説明のためにコードをわかりやすくするためです。このサンプル コードは、Project Server のテスト インストール上で実行するテスト アプリケーションとしてのみ提供されていますが、PSI を使用するセクションに関して例外ハンドラーを用意する必要があります。PSI に対する WCF 呼び出しの典型的な例外は、EndpointNotFoundException と CommunicationException です。

サンプル コードでは、通信用に基本的な HTTP トランスポートを使用しています。TCP ネットワーク トランスポートを使用するクライアント エンドポイントを作成し、このアプリケーションを外部のコンピューター上で実行して速度を比較することもできます。その外部コンピューターには .NET Framework 3.5 SP1 がインストールされている必要があります。

関連項目

タスク

チュートリアル : PWA リボンのカスタマイズと JS グリッドのアクセス

[方法] QueueSystem サービスを使用する

概念

WCF と PSI の概要

WCF ベースのコード サンプルの前提条件

その他のリソース

Programming WCF Security

Claims-Based Architectures

Transport Security Overview

Beginner's Guide to Windows Communication Foundation

What's New: Client Object Model