Windows PowerShell in DinnerNow

This article illustrates how Windows PowerShellâ„¢ is used to retrieve management information about Windows Communication Foundation (WCF) services, using code from the DinnerNow.net sample application. In addition, the article shows the Windows PowerShell hosting code that allows a host application to access this information. The companion topic, Developing with Windows PowerShell, provides background information.

Overview

DinnerNow.net is a sample application that revolves around a fictitious Web site where customers can search for and place a food order, split between multiple restaurants, for delivery to their home or office. A Microsoft Management Console (MMC) snap-in allows viewing of the WCF services and Windows Workflow Foundation (WF) workflows used in the application. The MMC snap-in hosts Windows PowerShell to provide the data used in populating the MMC snap-in views.

For information on the DinnerNow MMC snap-in, see Developing with Microsoft Management Console. For information on the DinnerNow WCF services, see Developing Connected Systems.

The source code for the DinnerNow.net application can be downloaded from CodePlex/DinnerNow/Releases. The source code shown in this article is from the DinnerNow.Management.PS project in the DinnerNow - Management solution, unless specified otherwise.

Note: The included code examples, taken from DinnerNow's source code, are usually not complete and are modified for readability and succinctness.

Windows PowerShell

The following sections describe the process of creating, installing, and using the Windows PowerShell types and cmdlets to retrieve information about the DinnerNow services.

  • Data types

    • DinnerNowService

    • WCFEndpoint

  • Cmdlets

    • Get-DNService

    • Get-DNEndPoint

  • Creating the Windows PowerShell snap-in

  • Hosting Windows PowerShell

    • GetDNServices method

    • GetEndpoints method

Data Types

The following data types are used to represent management information about the DinnerNow services and their endpoints. The following code is from Data\DinnerNowService.cs.

using System.Diagnostics;

public class DinnerNowService
{
    public string Name;
    public string CounterInstanceName;
    public string Status = "Stopped";
    public int ProcessId;
    public DateTime StartTime;
    public string DistinguishedName;
    public long Totalcalls
    {
        get
        { 
            PerformanceCounter calls = new PerformanceCounter(
                "ServiceModelService 3.0.0.0", 
                "Calls", CounterInstanceName, true);
            return calls.RawValue;
        }
    }
}

public class WCFEndpoint
{
    public string Address;
    public string CounterInstanceName;
    public string Name;
    public long Totalcalls
    {
        get
        {
            PerformanceCounter calls = new PerformanceCounter(
                "ServiceModelEndpoint 3.0.0.0", 
                "Calls", CounterInstanceName, true);
            return calls.RawValue;
        }
    }
}

Cmdlets

Two Windows PowerShell cmdlets, Get-DNService and Get-DNEndPoint, are used to retrieve the DinnerNow service information. This information is stored in collections of the preceding data types and used to populate the Services view of the DinnerNow MMC snap-in.

Cc303701.collapse_all(en-us,MSDN.10).gifGet-DNService Cmdlet

The Get-DNService cmdlet is derived from PSCmdlet and decorated with the CmdletAttribute attribute. The Get verb is used to indicate that data is retrieved by the cmdlet. The following code is from Get-DNService.cs.

using System.Collections.ObjectModel;
using System.Management;
using System.Management.Automation;
using DinnerNow.Management.PS.Data;

[Cmdlet(VerbsCommon.Get, "DNService")]
public class GetDNService : PSCmdlet
{
    private Collection<DinnerNowService> _services =
        new Collection<DinnerNowService>();

    protected override void ProcessRecord() { ... }
}

As shown in the following code example, the ProcessRecord method first loads a collection of DinnerNowService instances. Next, it creates a WMI Query Language (WQL) query, which is used to create an instance of the ManagementObjectSearcher class. The scope of the search is set to the \\.\root\ServiceModel WMI namespace. The ManagementObjectSearcher.Get method retrieves a collection of management objects based on the query and scope, which, in this case, is all the WCF services. The subset of WCF services that are DinnerNow WCF services is determined by comparing the management object names with the names of the DinnerNowService instances in the _services collection. Properties of the management objects set the DinnerNowService properties. Finally, the Cmdlet.WriteObject method writes the DinnerNow service information to the output pipeline.

protected override void ProcessRecord()
{
    try
    {
        _services.Add(new DinnerNowService("OrderService"));
        _services.Add(new DinnerNowService("RestaurantOrderService"));
        // ...

        SelectQuery query = new SelectQuery("Service");
        ManagementObjectSearcher l = new ManagementObjectSearcher(query);
        l.Scope = new ManagementScope(@"\\.\root\ServiceModel");

        foreach (ManagementObject current in l.Get())
        {
            foreach (DinnerNowService service in _services)
            {
                if (service.Name.Equals(current["Name"].ToString()))
                {
                    service.CounterInstanceName =
                        current["CounterInstanceName"].ToString();
                    service.Status = "Running";
                    service.ProcessId =
                        int.Parse(current["ProcessId"].ToString());
                    service.StartTime = WMIDateStringToDateTime(
                        current["Opened"].ToString());
                    service.DistinguishedName =
                        current["DistinguishedName"].ToString();
                }
            }
        }
        WriteObject(_services, true);
    }
}

Cc303701.collapse_all(en-us,MSDN.10).gifGet-DNEndPoint Cmdlet

The Get-DNEndPoint cmdlet works similar to the Get-DNService cmdlet except that the WQL query is different and Get-DNEndPoint uses a cmdlet parameter that retrieves a value from the input pipeline. The Name property is declared a cmdlet parameter by the application of the ParameterAttribute attribute. The Position property is set to zero and the ValueFromPipelineByPropertyName property is set to true, which indicates that the cmdlet parameter is set from the Name property of the first piped-in object. For more information on cmdlet parameters, see Developing with Windows PowerShell. The following code is from Get-DNEndPoint.cs.

[Cmdlet(VerbsCommon.Get, "DNEndPoint")]
public class GetDNEndPoint : PSCmdlet
{
    private string _Name = "";

    [Parameter(
        Position = 0,
        Mandatory = true,
        ValueFromPipelineByPropertyName = true,
        HelpMessage = "Name of the service")]
    [ValidateNotNullOrEmpty]
    public string Name
    {
        get { return _Name; }
        set { _Name = value; }
    }

    protected override void ProcessRecord()
    {
        Collection<DinnerNow.Management.PS.Data.WCFEndpoint> _endpoints =
            new Collection<DinnerNow.Management.PS.Data.WCFEndpoint>();
        // ...
        WriteObject(_endpoints, true);
    }
}

Creating the Windows PowerShell Snap-in

A Windows PowerShell snap-in registers custom cmdlets, providers, types, and formats. The DinnerNow Windows PowerShell snap-in, DinnerNowPSSnapIn, is derived from the PSSnapIn class and decorated with the RunInstallerAttribute attribute set to true.

The DinnerNow.Management.PS.Format.ps1xml file specified in the Formats property defines the display formatting for the DinnerNowService and WCFEndpoint custom data types. The following code is from PSSnapin.cs.

using System.ComponentModel;
using System.Management.Automation;

[RunInstaller(true)]
public class DinnerNowPSSnapIn : PSSnapIn
{
    public override string Name
    {
        get { return "DinnerNowPS"; }
    }
    public override string Description
    {
        get { return "Registers the CmdLets and Providers in this assembly"; }
    }
    public override string Vendor
    {
        get { return "Fabrikam"; }
    }

    public override string[] Formats
    {
        get
        {
            return new string[] {"DinnerNow.Management.PS.Format.ps1xml"};
        }
    }
    public override string[] Types
    {
        get
        {
            return new string[] { "DinnerNow.Management.PS.Types.ps1xml" };
        }
    }
}

The Windows PowerShell snap-in must be installed in order to register the cmdlets, types, and formats. The installation of the DinnerNowPS Windows PowerShell snap-in is performed as part of the DinnerNow.net setup process. Once the DinnerNowPS Windows PowerShell snap-in is installed, it can be added to a Windows PowerShell session using the Add-PSSnapin command. The Get-DNService and Get-DNEndPoint cmdlets can then be called and the results displayed in the command window.

Hosting Windows PowerShell

An application can host the Window PowerShell runtime in order to programmatically execute the Get-DNService and Get-DNEndPoint cmdlets. This functionality is provided by the PSHelper class, which creates and opens a Windows PowerShell execution environment. The DinnerNowPS Windows PowerShell snap-in is added to the execution environment, which allows the execution of Get-DNService and Get-DNEndPoint.

The following code is from PSHelper.cs in the DinnerNow.Management.MMC project. DinnerNow calls this code from its MMC snap-in to populate the view of the Services node.

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using DinnerNow.Management.PS.Data;

public class PSHelper
{
    private static RunspaceConfiguration config;
    private static Runspace runspace;
    public static string Database;
    public static string Server;
    public static string WorkflowFolder;

    static PSHelper()
    {
        config = RunspaceConfiguration.Create();
        PSSnapInException warning;
        config.AddPSSnapIn("DinnerNowPS", out warning);

        runspace = RunspaceFactory.CreateRunspace(config);
        runspace.Open();
    }
    
    public static IList<DinnerNowService> GetDNServices()
    { ... }
    public static IList< WCFEndpoint> GetEndpoints(string serviceName)
    { ... }
}

The two static methods, GetDNServices and GetEndpoints, execute the Get-DNService and Get-DNEndPoint cmdlets, respectively.

Cc303701.collapse_all(en-us,MSDN.10).gifGetDNServices Method

The GetDNServices method creates a Pipeline object and loads the object with the command that executes the Get-DNService cmdlet. Next, GetDNServices invokes the pipeline, which returns the results from running the cmdlet. The results are added to a collection of DinnerNowService instances, which are then returned.

public static IList<DinnerNowService> GetDNServices()
{
    Pipeline pipe = runspace.CreatePipeline();
    Command command = new Command("Get-DNService");
    pipe.Commands.Add(command);

    Collection<PSObject> results = pipe.Invoke();

    Collection<DinnerNowService> instances =
        new Collection<DinnerNowService>();
    foreach (PSObject result in results)
    {
        instances.Add(result.BaseObject as DinnerNowService);
    }
    return instances;
}

Cc303701.collapse_all(en-us,MSDN.10).gifGetEndpoints Method

The GetEndpoints method works similar to the way the GetDNServices method works, but adds a CommandParameter object to the command that executes the Get-DNEndPoint cmdlet. The Name cmdlet parameter of the Get-DNEndPoint cmdlet receives its value from this CommandParameter object.

public static IList<WCFEndpoint> GetEndpoints(string serviceName)
{
    Pipeline pipe = runspace.CreatePipeline();
    Command command = new Command("Get-DNEndPoint");

    command.Parameters.Add(new CommandParameter("Name", serviceName));
    pipe.Commands.Add(command);

    Collection<PSObject> results = pipe.Invoke();
    Collection<WCFEndpoint> instances = new Collection<WCFEndpoint>();
    foreach (PSObject result in results)
    {
        instances.Add(result.BaseObject as WCFEndpoint);
    }
    return instances;
}

See Also

Concepts

Design Highly Manageable Applications

Developing with Windows PowerShell

Developing with Microsoft Management Console

Developing Connected Systems

Other Resources

DinnerNow on CodePlex