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=""http://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=""http://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.