Using the SDK to Create and Edit Objects and Relationships Using Type Projections

One of the things that people often ask about is how to use the Service Manager SDK to:
  • create new objects, 
  • update objects,
  • add relationships, 
  • remove relationships
One of the keys to using the SDK in this way is to use type projections.  
We have had a few blog posts on this in the past in introduce the concepts and examples:
Getting Started with Type Projections
More with Type Projections
Getting and Working with Type Projections – Basic
These blogs posts were a little abstract though.  
This blog post shows you how to work with type projections using incident management as an example. 
The Visual Studio project is attached for reference.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.EnterpriseManagement;
using Microsoft.EnterpriseManagement.Common;
using Microsoft.EnterpriseManagement.Configuration;
using Microsoft.EnterpriseManagement.Packaging;
using Microsoft.EnterpriseManagement.ConnectorFramework;

namespace IncidentTypeProjection
{
    class Program
    {
        static void Main(string[] args)
        {
            //First get a connection to the Management Group
            EnterpriseManagementGroup emg = new EnterpriseManagementGroup("localhost");
            
            //======================================================
            //Example #1 - Creating a single incident object
            //======================================================
            Console.WriteLine("Creating an incident object....");
            //Get the System.WorkItem.Incident class
            ManagementPackClass classIncident = 
                emg.EntityTypes.GetClass(new Guid("A604B942-4C7B-2FB2-28DC-61DC6F465C68")); 
                
            
            //Also get the Medium urgency and impact enums 
            //since they are required values to create a new incident
            //More information on working with enums here: 
            //http://blogs.technet.com/b/servicemanager/archive/2010/05/25/programmatically-working-with-enumerations.aspx 
            ManagementPackEnumeration enumUrgencyMedium = 
                emg.EntityTypes.GetEnumeration(new Guid("02625C30-08C6-4181-B2ED-222FA473280E"));
            ManagementPackEnumeration enumImpactMedium = 
                emg.EntityTypes.GetEnumeration(new Guid("80CC222B-2653-2F68-8CEE-3A7DD3B723C1"));

            //Now create a CreatableEnterpriseManagementObject so we can create a 
            //new incident object and populate its properties and Commit() it.
            CreatableEnterpriseManagementObject cemoIncident = 
                new CreatableEnterpriseManagementObject(emg,classIncident);
            //Just doing this for demo purposes.  Obviously a GUID is not a good display name!
            String strTestID = Guid.NewGuid().ToString();  
            //Set some property values
            cemoIncident[classIncident, "DisplayName"].Value = strTestID;
            cemoIncident[classIncident, "Title"].Value = strTestID;
            cemoIncident[classIncident, "Urgency"].Value = enumUrgencyMedium;
            cemoIncident[classIncident, "Impact"].Value = enumImpactMedium;
            //And submit...
            cemoIncident.Commit();
            Console.WriteLine("Incident object created named: " + strTestID);

            
            //======================================================
            //Example #2 - Creating a Relationship between to Objects - Incidnet and User via the Affected User relationship
            //======================================================
            //Get the incident
            Console.WriteLine("Creating an relationship between the incidena and a user....");
            String strIncidentByTitleCriteria = 
                String.Format(@"<Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">" + 
                      "<Expression>" +
                        "<SimpleExpression>" +
                          "<ValueExpressionLeft>" +
                            "<Property>$Target/Property[Type='System.WorkItem.Incident']/Title$</Property>" + 
                          "</ValueExpressionLeft>" + 
                          "<Operator>Equal</Operator>" +
                          "<ValueExpressionRight>" +
                            "<Value>" + strTestID + "</Value>" +
                          "</ValueExpressionRight>" +
                        "</SimpleExpression>" +
                      "</Expression>" +
                    "</Criteria>");

            //System.WorkItem.Incident.Library MP
            ManagementPack mpIncidentLibrary = 
                emg.ManagementPacks.GetManagementPack(new Guid("DD26C521-7C2D-58C0-0980-DAC2DACB0900"));  

            //Get the incident using the criteria from above...
            EnterpriseManagementObjectCriteria emocIncidnetByTitle = 
                new EnterpriseManagementObjectCriteria((String)strIncidentByTitleCriteria,classIncident,mpIncidentLibrary,emg);
            IObjectReader<EnterpriseManagementObject> readerEmoIncidentAffectedUsers = 
                emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(emocIncidnetByTitle,ObjectQueryOptions.Default);
            EnterpriseManagementObject emoIncident = readerEmoIncidentAffectedUsers.ElementAt(0);

            //Get a user..
            //System.Domain.User class
            ManagementPackClass classUser = 
                emg.EntityTypes.GetClass(new Guid("ECA3C52A-F273-5CDC-F165-3EB95A2B26CF")); 
            IObjectReader<EnterpriseManagementObject> readerEmoDomainUsers = 
                emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(classUser,ObjectQueryOptions.Default);
            //Just getting whatever the first user is that comes back for demo purposes
            EnterpriseManagementObject emoDomainUser = readerEmoDomainUsers.ElementAt(0); 

            //Create a Relationship between the service and the user
            //System.WorkItemAfectedUser relationship type
            ManagementPackRelationship relIncidentAffectedUser = 
                emg.EntityTypes.GetRelationshipClass(new Guid("DFF9BE66-38B0-B6D6-6144-A412A3EBD4CE"));
            CreatableEnterpriseManagementRelationshipObject cemroIncidentAffectedUser = 
                new CreatableEnterpriseManagementRelationshipObject(emg, relIncidentAffectedUser);
            //Set the source and target...
            cemroIncidentAffectedUser.SetSource(emoIncident);
            cemroIncidentAffectedUser.SetTarget(emoDomainUser);
            //And submit...
            cemroIncidentAffectedUser.Commit();
            Console.WriteLine("Relationship created.");
            
            //======================================================
            //Example #3 - Creating relationships in bulk using Incremental Discovery Data
            //======================================================
            Console.WriteLine("Creating relationships to affected Windows computers...");

            //First get the class and relationship type we want to work with
            //Microsoft.Windows.Computer class
            ManagementPackClass classWindowsComputer = 
                emg.EntityTypes.GetClass(new Guid("EA99500D-8D52-FC52-B5A5-10DCD1E9D2BD"));
            //System.WorkItemAboutConfigItem relationship type
            ManagementPackRelationship relAffectedConfigurationItem = 
                emg.EntityTypes.GetRelationshipClass(new Guid("B73A6094-C64C-B0FF-9706-1822DF5C2E82")); 

            //Now create a "bucket" (IncrementalDiscoveryData class) for putting 
            //updates into so we can submit them all at once.
            IncrementalDiscoveryData iddRelationshipsToAdd = new IncrementalDiscoveryData();
            
            //Get all the Windows computers in the system.  
            //This is not typical.  Just doing this for demo purposes only.
            IObjectReader<EnterpriseManagementObject> readerEmoWindowsComputers = 
                emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(classWindowsComputer,ObjectQueryOptions.Default);
            foreach (EnterpriseManagementObject emoWindowsComputer in readerEmoWindowsComputers)
            {
                CreatableEnterpriseManagementRelationshipObject cemroAffectedConfigurationItem = 
new CreatableEnterpriseManagementRelationshipObject(emg, relAffectedConfigurationItem);
                //Set the source and target...
                cemroAffectedConfigurationItem.SetSource(emoIncident);
                cemroAffectedConfigurationItem.SetTarget(emoWindowsComputer);
                //Add it to the bucket...
                iddRelationshipsToAdd.Add(cemroAffectedConfigurationItem);
            }

            //And submit...
            iddRelationshipsToAdd.Overwrite(emg);
            Console.WriteLine("Relationships created.");

            
            //======================================================
            //Example #4 - Getting type projection objects and iterating through them
            //======================================================
            Console.WriteLine("Getting incidents and showing the users computers that are affected by them...");
            
            //Getting incidents and then list the computers and users they are affecting
            //System.WorkItem.Incident.ProjectionType
            ManagementPackTypeProjection mptpIncident = 
                emg.EntityTypes.GetTypeProjection(new Guid("285CB0A2-F276-BCCB-563E-BB721DF7CDEC")); 
            ObjectProjectionCriteria opcIncident = new ObjectProjectionCriteria(mptpIncident);
            IObjectProjectionReader<EnterpriseManagementObject> oprIncidents = 
                emg.EntityObjects.GetObjectProjectionReader<EnterpriseManagementObject>(opcIncident, ObjectQueryOptions.Default);
            foreach (EnterpriseManagementObjectProjection emopIncident in oprIncidents)
            {
                if (emopIncident[relIncidentAffectedUser.Target].Count > 0)
                {
                    //Show the affected user
                    Console.WriteLine(emopIncident.Object.DisplayName);
                }
                foreach (IComposableProjection icpAffectedConfigurationItem in emopIncident[relAffectedConfigurationItem.Target])
                {
                    //Show each of the affected configuration items
                    Console.WriteLine("\t" + icpAffectedConfigurationItem.Object.DisplayName);
                }
            }

            Console.WriteLine("Done showing incidents with related users and computers.");
            
            
            //======================================================
            //Example #5 - Creating objects and relationships at the same time via type projection
            //======================================================
            Console.WriteLine("Creating an incident and its relationships via a type projection...");
            //First create the seed object
            EnterpriseManagementObjectProjection emopIncidentToCreate = new EnterpriseManagementObjectProjection(emg, classIncident);

            //Just using this for testing.  Obviously using a GUID for a incidnet display name is not a good idea...
            String strIncidentName = Guid.NewGuid().ToString(); 
            //Set some properties on the object
            emopIncidentToCreate.Object[classIncident, "DisplayName"].Value = strIncidentName;
            emopIncidentToCreate.Object[classIncident, "Title"].Value = strIncidentName;
            emopIncidentToCreate.Object[classIncident, "Impact"].Value = enumImpactMedium;
            emopIncidentToCreate.Object[classIncident, "Urgency"].Value = enumUrgencyMedium;

            //Then relate it to other objects as needed
            IObjectReader<EnterpriseManagementObject> readerUsers = 
                emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(classUser, ObjectQueryOptions.Default);
            
            foreach (EnterpriseManagementObject emoUser in readerUsers )
            {
                emopIncidentToCreate.Add(emoUser,relAffectedConfigurationItem.Target);
            }

            //And submit...
            emopIncidentToCreate.Overwrite();
           
            Console.WriteLine("Incident and relationships created...");
            
            
            //======================================================
            //Example #6 - Updating objects and relationships at the same time via type projection
            //======================================================
            Console.WriteLine("Updating an incident and its relationships at the same time via a type projection...");
            //First get the EnterpriseManagmentObjectProjection
            IObjectProjectionReader<EnterpriseManagementObject> oprIncidentsToUpdate = 
                emg.EntityObjects.GetObjectProjectionReader<EnterpriseManagementObject>(opcIncident, ObjectQueryOptions.Default);
            foreach (EnterpriseManagementObjectProjection emopIncident in oprIncidentsToUpdate)
            {
                //Update a property
                emopIncident.Object[classIncident, "DisplayName"].Value = 
                    emopIncident.Object[classIncident, "DisplayName"].Value + " - Updated";
                //Remove a relationship
                foreach (IComposableProjection icpAffectedUser in emopIncident[relIncidentAffectedUser.Target])
                {
                    icpAffectedUser.Remove();
                }
                //Add a relationship
                //Just getting the first user here as an example
                EnterpriseManagementObject emoUserToAdd = 
                    emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(classUser,ObjectQueryOptions.Default).ElementAt(0); 
                emopIncident.Add(emoUserToAdd, relIncidentAffectedUser.Target);
            }
        }
    }
}