Getting and Working With Type Projections - Basic

In my last post in gave you a more in-depth look at creating type projections in Service Manager. In this post, I'd like to provide you with some basic examples on how you can retrieve and work with instances of type projections. I'll be using the same Management Pack and data set from the previous post.

On the Instances interface you will find a few methods for retrieving instances of EnterpriseManagementObjectProjection. Post Beta 1, we've actually cleaned up this interface significantly so I don't want to concentrate too much on the actual method call, but rather the parameters to it and working with the result.

In Beta 1 the method you would use looks like this:

 IList<EnterpriseManagementObjectProjection> GetObjectProjections<T>(ManagementPackTypeProjection managementPackTypeProjection, 
ObjectProjectionCriteria criteria, ObjectQueryOptions queryOptions) where T : EnterpriseManagementObject;

Post Beta 1, this method is replaced by:

 IObjectProjectionReader<T> GetObjectProjectionReader<T>(ObjectProjectionCriteria criteria, ObjectQueryOptions queryOptions)
            where T : EnterpriseManagementObject;

The ManagementPackTypeProjection from the first simply gets rolled into the ObjectProjectionCriteria object and the return type gets changed to support buffered reads to help with performance, however, IObjectProjectionReader can actually be used like an IList (although it doesn't implement IList); I'll discuss the reader more once we ship Beta 2 later this year. My examples below will use the latest code since that will be around longer; hopefully it is easily transferable for you to the Beta 1 API.

The first thing you need in order to get a type projection instance, is the type projection definition. This narrows down the types of instances you will get back to those that fit the structure of the type projection:

       // Connect to the management group
      EnterpriseManagementGroup managementGroup =
            new EnterpriseManagementGroup("localhost");

      // Get the management pack
      ManagementPack nflManagementPack = 
            managementGroup.ManagementPacks.GetManagementPack("NFL", null, new Version("1.0.0.0"));

      // Get the type projection
      ManagementPackTypeProjection nflProjection =
            nflManagementPack.GetTypeProjection("NFL.Conference.All");

Very simply you can retrieve projections by specifying the type:

 IObjectProjectionReader<EnterpriseManagementObject> objectProjections =
      managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
      new ObjectProjectionCriteria(nflProjection), ObjectQueryOptions.Default);

This will retrieve all projections in the system of that structure. A projection exists if the root object of the projection exists, regardless of whether any of the components are found. If you want to limit your result somehow, you will need to specify criteria to limit your result set. One example of simple criteria you could add is matching on one of the generic properties of the root object:

             string displayNameCriteria = @"<Criteria xmlns=""https://Microsoft.EnterpriseManagement.Core.Criteria/"">
                  <Expression>
                    <SimpleExpression>
                      <ValueExpressionLeft>
                        <GenericProperty>DisplayName</GenericProperty>
                      </ValueExpressionLeft>
                      <Operator>Equal</Operator>
                      <ValueExpressionRight>
                        <Value>National Football Conference</Value>
                      </ValueExpressionRight>
                    </SimpleExpression>
                  </Expression>
                </Criteria>";

            IObjectProjectionReader<EnterpriseManagementObject> objectProjectionsByDisplayName =
                 managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                     new ObjectProjectionCriteria(displayNameCriteria, nflProjection, managementGroup), ObjectQueryOptions.Default);

This would limit the results to any projections whose root object had a display name matching "National Football Conference." The list of available generic properties for Service Manager is Id, Name, DisplayName, LastModified and new post Beta 1 CreatedDate and LastModifiedBy.

It is also possible to query on type specific properties of the root object:

             string propertyCriteria = @"<Criteria xmlns=""https://Microsoft.EnterpriseManagement.Core.Criteria/"">
                  <Expression>
                    <SimpleExpression>
                      <ValueExpressionLeft>
                        <Property>$Target/Property[Type='NFL.Conference']/Name$</Property>
                      </ValueExpressionLeft>
                      <Operator>Equal</Operator>
                      <ValueExpressionRight>
                        <Value>NFC</Value>
                      </ValueExpressionRight>
                    </SimpleExpression>
                  </Expression>
                </Criteria>";

            IObjectProjectionReader<EnterpriseManagementObject> objectProjectionsByProperty =
                 managementGroup.Instances.GetObjectProjectionReader<EnterpriseManagementObject>(
                     new ObjectProjectionCriteria(propertyCriteria, nflProjection, nflManagementPack, managementGroup), ObjectQueryOptions.Default);

Criteria can get more complicated than the above examples, including specifying criteria on the individual components of the projection, but an in-depth discussion will happen in a later post.

Once you get an EnterpriseManagementObjectProjection, you essentially have a collection of instances organized in a hierarchy as defined by the type projection. If you run the following code:

             foreach (EnterpriseManagementObjectProjection projection in objectProjections)
            {
                Console.WriteLine("Conference: {0}", projection.Object.DisplayName);
                foreach (IComposableProjection division in projection[nflConferenceContainsDivision.Target])
                {
                    Console.WriteLine("\tDivision: {0}", division.Object.DisplayName);
                    foreach (IComposableProjection team in division[divisionContainTeam.Target])
                    {
                        Console.WriteLine("\t\tTeam: {0}", team.Object.DisplayName);
                        foreach (IComposableProjection coach in team[coachCoachesTeam.Source])
                        {
                            Console.WriteLine("\t\t\tCoach: {0}", coach.Object.DisplayName);
                        }
                    }
                }
            }

You'll get a result that shows you the structure of the projection in memory:

Conference: American Football Conference Division: AFC West Team: Oakland Raiders Team: Kansas City Chiefs Team: San Diego Chargers Team: Denver Broncos Division: AFC East Team: Buffalo Bills Team: New England Patriots Team: New York Jets Team: Miami Dolphins Division: AFC South Team: Houston Texans Team: Jacksonville Jaguars Team: Tennessee Titans Team: Indianapolis Colts Division: AFC North Team: Cleveland Browns Team: Pittsburgh Steelers Team: Baltimore Ravens Team: Cincinnati Bengals Conference: National Football Conference Division: NFC North Team: Green Bay Packers Team: Chicago Bears Coach: Lovie Smith Team: Detroit Lions Team: Minnesota Vikings Division: NFC South Team: Carolina Panthers Team: Atlanta Falcons Team: Tampa Bay Buccaneers Team: New Orleans Saints Division: NFC East Team: New York Giants Team: Dallas Cowboys Team: Philadelphia Eagles Team: Washington Redskins Division: NFC West Team: St. Louis Rams Team: Seattle Seahawks Team: Arizona Cardinals Team: San Francisco 49ers

If the line is indented, it indicates a jump across a relationship. The hierarchy is organized by the relationship types that bring in each component; put another way, as you traverse from parent to child you are traversing a relationship of a particular relationship type in one direction. So, as you go from American Football Conference to AFC West , you are moving from "Conference" to "Division" on the NFL.ConferenceHostsDivision relationship type.

Each node in an EnterpriseManagementObjectProjection implements IComposableProjection which offers various ways at traversing through the hierarchy. The object is also IEnumerable<KeyValuePair<ManagementPackRelationshipEndpoint, IComposableProjection>>, which shows that the hierarchy is organized by the relationship endpoint that brings each node in. You also get a pointer to the actual object at the node:

         /// <summary>
        /// The current object in the projection.
        /// </summary>
        EnterpriseManagementObject Object
        {
            get;
        }

The role that brought the current object into the current projection:

         /// <summary>
        /// Gets the role of this projection, relative to its parent.
        /// </summary>
        /// <value></value>
        ManagementPackRelationshipEndpoint ObjectRole
        {
            get;
        }

The object that brought the current object into the current projection:

         /// <summary>
        /// The parent object, if any, for the current object in the projection.
        /// </summary>
        IComposableProjection ParentObject
        {
            get;
        }

And a few indexers to aid in traversal, one of which we used in the sample above:

         /// <summary>
        /// Access all IComposableProjection elements of this IComposableProjection, optionally recursively.
        /// </summary>
        IList<IComposableProjection> this[TraversalDepth traversalDepth]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by role name.
        /// </summary>
        IList<IComposableProjection> this[string roleName]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by role name.
        /// </summary>
        IList<IComposableProjection> this[string roleName, ManagementPackClass classConstraint]
        {
            get;
        }

        /// <summary>
        /// Gets the IComposableProjection child of the projection by id of the contained object
        /// </summary>
        IComposableProjection this[string roleName, Guid id]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by relationship role.
        /// </summary>
        IList<IComposableProjection> this[ManagementPackRelationshipEndpoint role]
        {
            get;
        }

        /// <summary>
        /// Access IComposableProjection elements of this IComposableProjection, by relationship role and a class constraint.
        /// </summary>
        IList<IComposableProjection> this[ManagementPackRelationshipEndpoint role, ManagementPackClass classConstraint]
        {
            get;
        }

        /// <summary>
        /// Gets the IComposableProjection child of the projection by id of the contained object
        /// </summary>
        IComposableProjection this[ManagementPackRelationshipEndpoint role, Guid id]
        {
            get;
        }

In future posts I'll discuss more advanced concepts around working with projections instances (they're IXPathNavigable!), a deeper look at criteria, creating and editing projections and a lot more. I hope that by the time Beta 2 ships later this year to have a comprehensive list of topics that help introduce you to new API concepts.