Using SDK Criteria for Querying Instances
Criteria offers a way of querying over instances and types with a specific filter. This post covers the different ways of using criteria to query instances. Querying over instances involves querying over specific instances or instances that are part of a relationship in a projection. There are two different classes that offer these functionalities.
namespace Microsoft.EnterpriseManagement.Common
public class EnterpriseManagementObjectCriteria : QueryCriteria<EnterpriseManagementObjectCriteria>, IObjectCriteria
{
}
public class ObjectProjectionCriteria
{
}
Class EnterpriseManagementObjectCriteria works on pure instances. Say, you wanted to query for work items and were looking for all work items that satisfied a particular condition.
Class ObjectProjectionCriteria is used for querying over instances based on some condition that spans over a relationship. Say, you wanted to query for all incidents that were assigned to a certain user with some criteria acting on the relationship “IncidentAssignedToUser”.
Constructors of both these classes (EnterpriseManagementObjectCriteria and ObjectProjectionCriteria) work on an input criteria parameter which is a schema based XML string, the schema of which varies slightly between both the classes.
Most likely your query will fall into one of these categories which correspond to a specific type of schema element in your criteria XML.Please refer to more specific examples below for a given scenario. These descriptions and the examples that followed are explained with an assumption that readers have knowledge of core concepts such as MP definitions, type projections, relationships and various types of relationships such as reference or containment.
- You are querying over an instance (that exists by itself or in a relationship) evaluating a property on it for a specific value. (Please refer to SimpleCriteriaType and CriteriaOperatorType in the schema)
- You are querying over an instance (that exists by itself or in a relationship) evaluating a property on it based on a regular expression. (Please refer to RegExCriteriaType and RegexOperatorType in the schema)
- You are querying over an instance (that exists by itself or in a relationship) evaluating a property in a similar fashion such as the SQL IN/NOT IN clause. (Please refer to type InType/NotInType in the schema)
- You want to check if an instance actually exists and not are necessarily filtering on a particular value. (Please refer to type UnaryCriteriaType and UnaryOperatorType in the schema)
- Your query spans over some form of Containment relationship. (Please refer to ContainsCriteriaType,NotContainsCriteriaType, ContainedCriteriaType, NotContainedCriteriaType in the schema)
- You are querying over an instance (that exists by itself or in a relationship) evaluating a property on it based on a full text expression. (Please refer to FullTextCriteriaType and FullTextOperatorType in the schema)
- You have a NOT expression in your criteria (Please refer to NotType Criteria).
- Your criteria involves performing a combination of the above functions. (Please refer to And Type, Or Type in the schema)
The combination of all examples stated below will give an understanding of some core concepts in criteria.
Example 1: Querying for an instance on some property using simple expression criteria, using EnterpriseManagementObjectCriteria class.
Consider a query for retrieving all instances of a certain class based on some property value. Say, fetching all incidents whose title contains the text “critical”. Since this is purely evaluating a particular property on the criteria we can construct an SDK criteria of type “SimpleCriteriaType” for the purpose. As per the schema all criteria start with an “ExpressionType”
All property evaluation is of the format: Context/ [Type]/ [PropertyName] (All start with a Context tag which is required and denotes the type of instance we are querying for, [Type] is replaced by the name of the type we are querying on, and in this case “System.WorkItem.Incident” and [PropertyName] is the name of the property which in this case is “Title”.
We replace the inner text of the <Value> tag with the actual value to be compared with. Since we are performing a Like operation the value is appended with % as in %critical%.
This forms criteria Xml as below:
<Criteria xmlns="https://Microsoft.EnterpriseManagement.Core.Criteria/">
<Expression>
<SimpleExpression>
<ValueExpressionLeft>
<Property>$Context/Property[Type='System.WorkItem.Incident']/Title$</Property>
</ValueExpressionLeft>
<Operator>Like</Operator>
<ValueExpressionRight>
<Value>%critical%</Value>
</ValueExpressionRight>
</SimpleExpression>
</Expression>
</Criteria>
This piece of code creates an EnterpriseManagementObjectCriteria object:
ManagementPack incidentManagementPack = managementGroup.GetManagementPack("System.WorkItem.Incident.Library", "31bf3856ad364e35", “1.0.0.0”);
ManagementPackClass incident = managementGroup.EntityTypes.GetClass("System.WorkItem.Incident", incidentManagementPack);
EnterpriseManagementObjectCriteria criteria = new EnterpriseManagementObjectCriteria(criteriaXml,incident, incidentManagementPack,managementGroup);
(CriteriaXml here indicates the xml based criteria string above)
You could also opt to use the constructor below, however since the management pack is excluded, the string criteria has to include the guid values for the class names and property names instead of the name.
EnterpriseManagementObjectCriteria criteria = new EnterpriseManagementObjectCriteria(criteriaXml, incident,managementGroup);
The same criteria with guid values for the type and property will look as shown below:
<Criteria xmlns="https://Microsoft.EnterpriseManagement.Core.Criteria/">
<Expression>
<SimpleExpression>
<ValueExpressionLeft>
<Property>$Context/Property[Type=8C61EEAB-9F24-0EB5-5B4A-BF4DD59C0EF3']/ 0C7DC36E-876F-8B2B-C53B-A6297208E6F0$</Property>
</ValueExpressionLeft>
<Operator>Like</Operator>
<ValueExpressionRight>
<Value>%critical%</Value>
</ValueExpressionRight>
</SimpleExpression>
</Expression>
</Criteria>
Example 2: Querying on related instances using Object Projection Criteria and usage of unary expression
The above example talks about retrieving instances of a particular class, however say you wanted to retrieve those instances based on a certain relation. Consider querying for all incidents that were related to a config item. These are instances that are part of the relationship “System.WorkItemRelatesToConfigItem”. Note that we need to query over this relationship here. Also, we are looking for incidents that are related to any config items that exist and not necessarily a config item of a particular property value. Hence we will be using “UnaryExpression” criteria in this case.
As per the schema the unary expression criteria looks like below:
<Criteria xmlns="https://Microsoft.EnterpriseManagement.Core.Criteria/">
<Expression>
<UnaryExpression>
<ValueExpression>
<GenericProperty Path="$Context/Path[Relationship='WorkItem!System.WorkItemRelatesToConfigItem']$">Id</GenericProperty>
</ValueExpression>
<Operator>IsNotNull</Operator>
</UnaryExpression>
</Expression>
</Criteria>
Unary expressions contain just an expression and an operator to check for null. Notice how we are using a generic property tag instead of a property tag here. This is used for querying over generic properties that are common to all instances and not just properties specific to that particular instance. Following are the list of generic properties that we can be used in this format.
public enum EnterpriseManagementObjectGenericPropertyName
{
Id,
Name,
Path,
FullName,
DisplayName,
LastModified,
TimeAdded,
LastModifiedBy
}
As long as the property is one of the above we can use a <GenericProperty> tag and need not specify the type in the criteria. The attribute Path indicates the relationship on which we are using the criteria. The Path usually starts with “Context/” and is followed by one or more relationships. We have just one in this case. You can use the above sdk criteria to build an Object Projection Criteria object.
Add a projection that defines the relationship - System.WorkItemRelatesToConfigItem in a custom management pack as below:
<TypeProjection ID="WiRelatesToCiProj" Accessibility="Public" Type="CoreIncident!System.WorkItem.Incident">
<Component Path="$Target/Path[Relationship='WorkItem!System.WorkItemRelatesToConfigItem']$" Alias="Rel" />
</TypeProjection>
The following code creates an ObjectProjectionCriteria :
ManagementPack workItemMp = managementGroup.GetManagementPack("System.WorkItem.Library", "31bf3856ad364e35", new Version("1.0.0.0"));
ManagementPack projMp = managementGroup.GetManagementPack("custom.Mp",null, new Version("1.0.0.0"));
ManagementPackTypeProjection workItemRelConfigProj =managementGroup.EntityTypes.GetTypeProjection("System.WorkItem.Incident.RelatesToConfig", projMp);
ObjectProjectionCriteria projCriteria = new ObjectProjectionCriteria(criteriaxml, workItemRelConfigProj, projMp, managementGroup);
(criteria xml is the above mentioned xml string).
Just like EnterpriseManagementObjectCtieria you can also use the constructor that does not take a management pack, but will need to specify guids for all elements.
Example 3: Using typeconstraints, references and tokens in criteria. Usage of And/Or criteria.
Consider the scenario where you are not only querying for workitems related to config items but for those that are related to a specific config item based on some property. Here’s where using a type constraint is useful. Consider the following criteria.
<Criteria xmlns="https://Microsoft.EnterpriseManagement.Core.Criteria/">
<Reference Id="System.Library" Version="1.0.0.0" Alias="System" />
<Expression>
<And>
<Expression>
<SimpleExpression>
<ValueExpressionLeft>
<Property>$Context/Path[Relationship='System.WorkItemRelatesToConfigItem' TypeConstraint='System!System.Computer']/Property[Type='System!System.Computer']/DisplayName$</Property>
</ValueExpressionLeft>
<Operator>Equal</Operator>
<ValueExpressionRight>
<Value>testmachine</Value>
</ValueExpressionRight>
</SimpleExpression>
</Expression>
<Expression>
<SimpleExpression>
<ValueExpressionLeft>
<GenericProperty Path="$Context/Path[Relationship='System.WorkItemAssignedToUser' SeedRole='Source']$">Id</GenericProperty>
</ValueExpressionLeft>
<Operator>Equal</Operator>
<ValueExpressionRight>
<Token>[me]</Token>
</ValueExpressionRight>
</SimpleExpression>
</Expression>
</And>
</Expression>
</Criteria>
Note :
Ø The typeconstraint restricts the filter to just config items of type System.Computer.
Ø The type “System.Computer” is referred with an alias “System!” since it’s defined in a different management pack. The reference is added to the existing MP.
Ø The criteria combines two different expression in an “AND” condition and hence queries for all workitems that are assigned to me (current user) and that also have a relationship with class “System.Computer”.
Ø The token [me] instead of the value to be compared with resolves to the current user. Other tokens that are allowed are :
[me] – denotes current user
[mygroups] – denotes current user’s group
[now] – denotes the current utc time.
[now + 5s], [now – 2h] – denotes some variations using s(seconds), d (days), m(months), h(hour).
Example 4: Using sdk criteria to create customizable views and usage of Containment criteria.
Say you have a containment relationship that you want to query over. Consider a containment relationship where you want to group all incidents related to hardware under a group “Hardware Incidents”.
Currently the SM console, does not offer a way for doing the same.
However, we can achieve the same by adding SDK criteria and customize the view.
Step 1) In the SM console, go to WorkItems -> IncidentManagement.
Step 2) Under Tasks choose “Create View”
Step 3) Edit the General and Display tabs, do not add any criteria under the Criteria tab. Enter view name as “Hardware Related Incidents”.
Step 4) Save the view. The view will show up under incident management and get saved in the unsealed management pack “Service Manager Incident Management Configuration Library”.
Step 5) Export this MP.
Step 6) Add the following class, relationship and type projection to the MP.
<ClassType ID="IncidentHardwareGroup" Base="WorkItem!System.WorkItemGroup" Abstract="false" Accessibility="Public" Hosted="false">
<Property ID="GroupId" Type="string" Key="true" />
</ClassType>
<RelationshipType ID="IncidentGroupContainsIncidents" Base="System!System.Containment" Abstract="false" Accessibility="Public">
<Source ID="incidentGrp" Type=" IncidentHardwareGroup " />
<Target ID="incident" Type="CoreIncident!System.WorkItem.Incident" />
</RelationshipType>
<TypeProjection ID="IncidentGrpProj" Accessibility="Public" Type="CoreIncident!System.WorkItem.Incident">
<Component Path="$Target/Path[Relationship=' IncidentGroupContainsIncidents ' SeedRole='Target']$" Alias="group" />
</TypeProjection>
Step 7) Under the definition generated for this view you will notice an empty criteria tag (<Criteria/>), since we did not pick a criteria while creating this view.
Step 8) Replace <Criteria/> with the criteria below.
<Criteria>
<QueryCriteria Adapter="omsdk://Adapters/Criteria" xmlns="https://tempuri.org/Criteria.xsd">
<Criteria>
<FreeformCriteria>
<Freeform>
<Criteria xmlns="https://Microsoft.EnterpriseManagement.Core.Criteria/">
<Expression>
<Contained>
<Class>$MPElement[Name='System.IncidentCriticalGroup']$</Class>
</Contained>
</Expression>
</Criteria>
</Freeform>
</FreeformCriteria>
</Criteria>
</QueryCriteria>
</Criteria>
The highlighted portion is the actual sdk criteria, rest of the format is needed for UI purposes. If you wanted to have some criteria on the group as well, you could have a criteria such as:
<Criteria xmlns="https://Microsoft.EnterpriseManagement.Core.Criteria/">
<Expression>
<Contained>
<Class>$MPElement[Name='System.IncidentCriticalGroup']$</Class>
<Expression>
<SimpleExpression>
<ValueExpressionLeft>
<Property>$Context/Property[Type='System.IncidentCriticalGroup']/GroupId$</Property>
</ValueExpressionLeft>
<Operator>Equal</Operator>
<ValueExpressionRight>
<Value>HardwareRelated</Value>
</ValueExpressionRight>
</SimpleExpression>
</Expression>
</Contained>
</Expression>
</Criteria>
Step 9) Change the name of the type projection in the view to “IncidentGrpProj”.
Step 10) Import the MP.
Step 11) The view will get updated and show the filtered instances as below.
I have covered the important concepts in SDK criteria and examples on querying over instances using the same. Using SDK criteria for querying over types will be covered in a different post. Hope you find this useful.
Comments
Anonymous
April 05, 2011
Hi, I'm trying to put criteria on my TypeProjection so I only get the related records to an incident back. However, I'm always getting an exception stating that the provided path is invalid. Unfortunately, I don't have a clue on what's wrong. Here's my relevant xml for the mp: <TypeDefinitions> <EntityTypes> <ClassTypes> <ClassType ID="WorkEffort" Extension="false" Singleton="false" Hosted="false" Base="Supporting.Library!System.SupportingItem" Abstract="false" Accessibility="Public"> <Property ID="WorkEffortId" Required="false" MinLength="0" MaxLength="256" CaseSensitive="false" Key="true" AutoIncrement="false" Type="guid"/> <Property ID="EffortDate" Required="false" MinLength="0" MaxLength="256" CaseSensitive="false" Key="false" AutoIncrement="false" Type="datetime"/> <Property ID="TimeWorked" Required="true" MinLength="0" MaxLength="256" CaseSensitive="false" Key="false" AutoIncrement="false" Type="int" DefaultValue="0" MinValue="0"/> <Property ID="StandBy" Required="true" MinLength="1" MaxLength="1" CaseSensitive="false" Key="false" AutoIncrement="false" Type="int" DefaultValue="0" MinValue="0" MaxValue="1"/> <Property ID="CostCenter" Required="false" MinLength="0" MaxLength="256" CaseSensitive="false" Key="false" AutoIncrement="false" Type="string"/> </ClassType> </ClassTypes> <RelationshipTypes> <RelationshipType ID="WorkEffort.AssignedUser" Base="System!System.Reference" Abstract="false" Accessibility="Public" > <Source ID="WE_ADUser_Source" Type="WorkEffort" MaxCardinality="1" MinCardinality="1"/> <Target ID="WE_ADUser_Target" Type="Windows.Library!Microsoft.AD.User" MaxCardinality="1" MinCardinality="1"/> </RelationshipType> <RelationshipType ID="WorkEffort.AffectedIncident" Base="System!System.Reference" Abstract="false" Accessibility="Public"> <Source ID="WE_Incident_Source" Type="WorkEffort" MaxCardinality="2147483647" MinCardinality="0"/> <Target ID="WE_Incident_Target" Type="Incidents!System.WorkItem.Incident" MaxCardinality="1" MinCardinality="0"/> </RelationshipType> </RelationshipTypes> <TypeProjections> <TypeProjection ID="Incident.WorkEffort.All" Accessibility="Public" Type="WorkEffort"> <Component Path="$Target/Path[Relationship='WorkEffort.AssignedUser']$" Alias="AssignedUser" /> <Component Path="$Target/Path[Relationship='WorkEffort.AffectedIncident']$" Alias="AffectedIncident" /> </TypeProjection> </TypeProjections> </EntityTypes> </TypeDefinitions> And here are my criteria: String criteria = String.Format(@" <Criteria xmlns=""microsoft.enterprisemanagement.core.criteria/""> <Reference Id=""WorkEffort.Registration"" Version=""1.0.0.0"" PublicKeyToken=""06f2f2b4e2ab1f8e"" Alias=""WorkEffort"" /> <Reference Id=""System.WorkItem.Incident.Library"" Version=""{0}"" PublicKeyToken=""{1}"" Alias=""Incidents"" /> <Expression> <SimpleExpression> <ValueExpressionLeft> <Property>$Context/Path[Relationship='WorkEffort.AffectedIncident' TypeConstraint='WorkItems!System.WorkItem.Incident']/Property[Type='Incidents!System.WorkItem.Incident']/Id$</Property> </ValueExpressionLeft> <Operator>Equal</Operator> <ValueExpressionRight> <Value>{2}</Value> </ValueExpressionRight> </SimpleExpression> </Expression> </Criteria>", SystemVersion.ToString(), SystemPublicKeyToken, incident.Name); Could you please take a look? Thanks, KrisAnonymous
May 04, 2011
You don't require a type constraint in your criteria. Reason being, type constraint is mainly useful when you want to add a filter on the target of a relationship if the target of the relationship is different from the TYPE you are querying on. It doesn't matter if you used one, but the thing is here the alias on the type constraint seems incorrect. It should be TypeConstraint='Incidents!System.WorkItem.Incident' instead of TypeConstraint='WorkItems!System.WorkItem.Incident'.Anonymous
August 30, 2011
The comment has been removedAnonymous
September 09, 2011
Thanks for the catch. Yes, [today] token is not supported. I have updated the content. -Subha