none
Assigning reviewers programmatically

    Question

  • Hi all, I wrote a simple aspx page (C#) that creates a change and applies a template to it.

    This template has a blank review activity attached to it, without a reviewer list.

    Is it possible, by C# code, to assign a user (of course I already know its username) as the reviewer of the activity provided by applying the change template?

     

    Thanks for any suggestion :)

    Wednesday, December 29, 2010 2:06 PM

Answers

  • A little late, but here's the sample code :) While working on this, I discovered a way to reduce the complexity of my original suggestion. You don't have to create the change request then find it again. You can relate a "not quite created" reviewer to the change request you're creating with the template. When you finally create the change request (using the Overwrite method), it will create the reviewer right along with it in one shot. So, for this particular scenario, there shouldn't be any need for a temporary reviewer. (Comp-Sci geek-speak warning: In other words, if the change request, activity, and reviewer are all created and related in a single transaction, as I hope is the case, then there is no need to worry about a race condition with the activity management workflow)

    There are a couple things to point out about this sample code. You'll have to provide the AD UserName (strADUserName). This is the UserName that's in the System.AD.User class object in Service Manager. You can get that from your web page drop-down list control. Secondly, you should already have an EnterpriseManagementGroup object connected, so you can ignore mine :) Also, in this sample code, you'll notice I'm creating a Change Request from an undefined template Guid. It sounds like you're already creating a change request from a template in your web page, so just substitute my object with yours. I'm not sure how you're creating your object from a template, but if you're using EnterpriseManagementObjectProjection, then you'll be in good shape.

    I suggest you test out this code on your own to become familiar with it, then apply what you've learned to your web page in order to assign your reviewer.

    Later this weekend, I might think of a dozen other things I should point out so I'll update this post if I do :)
    Oh, and this is all in C#..if you need VB, it shouldn't be hard to translate. I hope this helps a little bit! :) Oh..and I have to say there's no warranty with this code (I really hate saying that ;) ).

      //Here's the AD User hook. Your web page control will have to make the actual UserName available somehow.
      String strADUserName=<SomeADUserName>;
      Guid guidTemplate = new Guid(<YourTemplateGuid>);
    
      //You'll need some object query options.
      ObjectQueryOptions objQueryOpts = new ObjectQueryOptions();
      objQueryOpts.ObjectRetrievalMode = ObjectRetrievalOptions.Buffered;
      objQueryOpts.DefaultPropertyRetrievalBehavior = ObjectPropertyRetrievalBehavior.All;
      objQueryOpts.MaxResultCount = 250;
    
      //First get a connection to the Management Group.
      EnterpriseManagementGroup emg = new EnterpriseManagementGroup(<YourManagementGroupServer>);
    
      //Here's the stuff you should already have. This is your selected object template and a new Change Request that you're creating with it.
      ManagementPackObjectTemplate mptCRTemplate = emg.Templates.GetObjectTemplate(guidTemplate);
      EnterpriseManagementObjectProjection emopNewCR = new EnterpriseManagementObjectProjection(emg, mptCRTemplate);
    
      //Get management packs. These will be used to clearly reference some management pack classes.
      ManagementPack mpMicrosoftWindowsLibrary = emg.ManagementPacks.GetManagementPack(new Guid("545131F0-58DE-1914-3A82-4FCAC9100A33")); //Microsoft.Windows.Library
      ManagementPack mpSystemWorkItemActivityLibrary = emg.ManagementPacks.GetManagementPack(new Guid("AA265D90-3E2E-B9A2-D929-BE0D36F1A53E")); //System.WorkItem.Activity.Library
    
      //Get management pack classes. You could just use the GUID's if you know them.
      ManagementPackClass mpcReviewActivity = emg.EntityTypes.GetClass("System.WorkItem.Activity.ReviewActivity", mpSystemWorkItemActivityLibrary);
      ManagementPackClass mpcADUser = emg.EntityTypes.GetClass("Microsoft.AD.User", mpMicrosoftWindowsLibrary);
      ManagementPackClass mpcReviewer = emg.EntityTypes.GetClass("System.Reviewer", mpSystemWorkItemActivityLibrary);
    
      //The relationships we'll need to relate our reviewer to an AD User and the review activity.
      ManagementPackRelationship mprReviewerIsUser = emg.EntityTypes.GetRelationshipClass("System.ReviewerIsUser", mpSystemWorkItemActivityLibrary);
      ManagementPackRelationship mprWorkItemContainsActivity = emg.EntityTypes.GetRelationshipClass("System.WorkItemContainsActivity", mpSystemWorkItemActivityLibrary);
      ManagementPackRelationship mprReviewActivityHasReviewer = emg.EntityTypes.GetRelationshipClass("System.ReviewActivityHasReviewer", mpSystemWorkItemActivityLibrary);
    
      //Here's the simple criteria that will let you get the AD User object from the SM CMDB
      string strADUserCriteria = String.Format(@"
      <Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
       <Reference Id=""Microsoft.Windows.Library"" PublicKeyToken=""{0}"" Version=""{1}"" Alias=""MSWinLib"" />
       <Expression>
       <SimpleExpression>
        <ValueExpressionLeft>
        <Property>$Target/Property[Type='MSWinLib!Microsoft.AD.User']/UserName$</Property>
        </ValueExpressionLeft>
        <Operator>Equal</Operator>
        <ValueExpressionRight>
        <Value>" + strADUserName + @"</Value>
        </ValueExpressionRight>
       </SimpleExpression>
       </Expression>
      </Criteria>
      ", mpMicrosoftWindowsLibrary.KeyToken, mpMicrosoftWindowsLibrary.Version.ToString());
    
      //Get the AD User
      EnterpriseManagementObjectCriteria emocADUser = new EnterpriseManagementObjectCriteria(strADUserCriteria, mpcADUser, emg);
      IObjectReader<EnterpriseManagementObject> orADUser = emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(emocADUser, objQueryOpts);
      //Since UserName is a key, this query should only ever return 1 result unless you have the same username on multiple domains.
      EnterpriseManagementObject emoADUser = orADUser.ElementAt(0); 
    
      //Now we create our reviewer. This is a projection because we'll need to relate this reviewer to a user and activity
      EnterpriseManagementObjectProjection emopReviewerToCreate = new EnterpriseManagementObjectProjection(emg, mpcReviewer);
      //This is up to you whether the user must vote or not. You can also set other properties here if you wish.
      emopReviewerToCreate.Object[mpcReviewer, "MustVote"].Value = true; 
      //Relate the recently found AD User to this new Reviewer.
      emopReviewerToCreate.Add(emoADUser, mprReviewerIsUser.Target);
    
      //Remember the new Change Request you're creating? Even though it hasn't been created yet, you can
      //loop through it's relations! This means you can find the review activity that's part of your template.
      foreach (IComposableProjection icpActivity in emopNewCR[mprWorkItemContainsActivity.Target])
      {
      //Since it's very likely that you have multiple activities in your template, you'll have to find the specific
      //review activity you wish to update. This code will update EVERY review activity. You can add additional contraints
      //here if you wish.
      //Notice that all we're doing here is comparing this activity to a review activity class object. It's a handy way
      //of verifying that whatever object you're looking at matches a class you're interested in. There's a slightly
      //shorter way of doing it, but this is how I've always done it :)
      if (emg.EntityTypes.GetClass(icpActivity.Object.LeastDerivedNonAbstractManagementPackClassId) == mpcReviewActivity)
      {
       //We've now found the review activity. Relate our new Reviewer to it. A nifty part is that
       //the reviewer doesn't have to exist yet! You can relate a pre-built object as easily as an existing object!
       icpActivity.Add(emopReviewerToCreate, mprReviewActivityHasReviewer.Target);
      }
      }
    
      //This builds the whole 9 yards. It creates your Change Request, the related activity, the reviewer
      //and creates all the relationships in one shot.
      emopNewCR.Overwrite();
    
    • Marked as answer by DashKappei Sunday, January 09, 2011 12:21 PM
    Friday, January 07, 2011 11:49 PM

All replies

  • Is the reviewer always going to be the same? If so, you can just add them as a reviewer to your template's activity.

    If the reviewer is dynamic or you just need to assign them programatically, yes, you can assign a reviewer to an activity.

    Reviewers are actually a separate class. It's not quite as simple as just relating an AD user to a review activity. In the console when you assign a reviewer to an activity, the system actually creates a new Reviewer instance and relates it to an AD (or domain) user that you selected. It then relates that reviewer instance to the review activity. This is necessary because the reviewer class has some properties specific and necessary for reviewing (afterall, it would clutter up the AD class to have "Decision" and "Must Vote" properties :) )

    So, in your code behind, after you've applied your template to your change request, you grab the ad/domain user you want as a reviewer, and you grab the review activity created for your change request. You then create a new instance of the reviewer class and relate it to your ad user (through the System.ReviewerIsUser relationship), and you relate it to your review activity through the System.ReviewActivityHasReviewers relationship. Commit the object and your review activity will now have a reviewer.

    The above is a conceptual view of how to do things, of course. If you want some more specifics, let me know (I've done this myself and it works fine :) )

    edit: There's an important point I should mention. Review activities with no reviewers will, unfortunately, complete automatically. So, if you apply a template to a change request and it creates a review activity with no reviewers, the review activity could complete before your program creates and relates the reviewers (it's a race condition between the service manager workflow that manages review activities and your program trying to add a reviewer to that activity).
    I solved it by adding a temporary reviewer to my template's review activity. When my program adds a new reviewer it also removes the temporary reviewer.

    Wednesday, December 29, 2010 2:51 PM
  • Is the reviewer always going to be the same? If so, you can just add them as a reviewer to your template's activity.

    If the reviewer is dynamic or you just need to assign them programatically, yes, you can assign a reviewer to an activity.

    Reviewers are actually a separate class. It's not quite as simple as just relating an AD user to a review activity. In the console when you assign a reviewer to an activity, the system actually creates a new Reviewer instance and relates it to an AD (or domain) user that you selected. It then relates that reviewer instance to the review activity. This is necessary because the reviewer class has some properties specific and necessary for reviewing (afterall, it would clutter up the AD class to have "Decision" and "Must Vote" properties :) )

    So, in your code behind, after you've applied your template to your change request, you grab the ad/domain user you want as a reviewer, and you grab the review activity created for your change request. You then create a new instance of the reviewer class and relate it to your ad user (through the System.ReviewerIsUser relationship), and you relate it to your review activity through the System.ReviewActivityHasReviewers relationship. Commit the object and your review activity will now have a reviewer.

    The above is a conceptual view of how to do things, of course. If you want some more specifics, let me know (I've done this myself and it works fine :) )

    edit: There's an important point I should mention. Review activities with no reviewers will, unfortunately, complete automatically. So, if you apply a template to a change request and it creates a review activity with no reviewers, the review activity could complete before your program creates and relates the reviewers (it's a race condition between the service manager workflow that manages review activities and your program trying to add a reviewer to that activity).
    I solved it by adding a temporary reviewer to my template's review activity. When my program adds a new reviewer it also removes the temporary reviewer.

     

    Thanks for your reply :)

    No, the reviewer will not be the same. We created a dynamic page, placed on the Self Service Portal, that implements a kind of service request that needs to be approved by a particular user selected through a combo box.
    The code behind creates the change by populating these fields:

    change title --> statically defined by the aspx code
    change description --> combo box selected by the Internet Explorer user
    change owner --> combo box selected by the Internet Explorer user (the change owner is our "approver" and also needs to be the change reviewer)
    change requestor --> the username of the Internet Explorer user

    Of course, both change requestor and change owner are provided as Active Directory usernames.

    Our code has been defined by following this example: http://blogs.technet.com/b/servicemanager/archive/2010/10/04/using-the-sdk-to-create-and-edit-objects-and-relationships-using-type-projections.aspx
    and it perfectly creates the change request and assigns to it the owner as well as the affected user/change requestor.

    So...
    What I need to know is how to programmatically grab the review activity assigned by the template and then how to assign the reviewer (which is also the change owner, selected by the IE user).
    Unfortunately, I'm not a developer at all so - without proper examples - I just don't know how to go on and I need more details about a solution for my problem :) :)

    P.S.: nice to know the tip about the temporary reviewer :)

    Monday, January 03, 2011 12:10 AM
  • Sorry I haven't replied with more details yet. I haven't forgotten! I want to setup a straight-forward working example for you before I post..and I can't do that on my boss' time ;)

    I'll put one together on my lunch breaks over the next day or two and get back to you :)

    I do want to point out that the example I'll show you will only be a variation on a lot other folks posts (Travis, Patrick, etc). Credit where credit is due :)

     

     

    Wednesday, January 05, 2011 9:37 PM
  • A little late, but here's the sample code :) While working on this, I discovered a way to reduce the complexity of my original suggestion. You don't have to create the change request then find it again. You can relate a "not quite created" reviewer to the change request you're creating with the template. When you finally create the change request (using the Overwrite method), it will create the reviewer right along with it in one shot. So, for this particular scenario, there shouldn't be any need for a temporary reviewer. (Comp-Sci geek-speak warning: In other words, if the change request, activity, and reviewer are all created and related in a single transaction, as I hope is the case, then there is no need to worry about a race condition with the activity management workflow)

    There are a couple things to point out about this sample code. You'll have to provide the AD UserName (strADUserName). This is the UserName that's in the System.AD.User class object in Service Manager. You can get that from your web page drop-down list control. Secondly, you should already have an EnterpriseManagementGroup object connected, so you can ignore mine :) Also, in this sample code, you'll notice I'm creating a Change Request from an undefined template Guid. It sounds like you're already creating a change request from a template in your web page, so just substitute my object with yours. I'm not sure how you're creating your object from a template, but if you're using EnterpriseManagementObjectProjection, then you'll be in good shape.

    I suggest you test out this code on your own to become familiar with it, then apply what you've learned to your web page in order to assign your reviewer.

    Later this weekend, I might think of a dozen other things I should point out so I'll update this post if I do :)
    Oh, and this is all in C#..if you need VB, it shouldn't be hard to translate. I hope this helps a little bit! :) Oh..and I have to say there's no warranty with this code (I really hate saying that ;) ).

      //Here's the AD User hook. Your web page control will have to make the actual UserName available somehow.
      String strADUserName=<SomeADUserName>;
      Guid guidTemplate = new Guid(<YourTemplateGuid>);
    
      //You'll need some object query options.
      ObjectQueryOptions objQueryOpts = new ObjectQueryOptions();
      objQueryOpts.ObjectRetrievalMode = ObjectRetrievalOptions.Buffered;
      objQueryOpts.DefaultPropertyRetrievalBehavior = ObjectPropertyRetrievalBehavior.All;
      objQueryOpts.MaxResultCount = 250;
    
      //First get a connection to the Management Group.
      EnterpriseManagementGroup emg = new EnterpriseManagementGroup(<YourManagementGroupServer>);
    
      //Here's the stuff you should already have. This is your selected object template and a new Change Request that you're creating with it.
      ManagementPackObjectTemplate mptCRTemplate = emg.Templates.GetObjectTemplate(guidTemplate);
      EnterpriseManagementObjectProjection emopNewCR = new EnterpriseManagementObjectProjection(emg, mptCRTemplate);
    
      //Get management packs. These will be used to clearly reference some management pack classes.
      ManagementPack mpMicrosoftWindowsLibrary = emg.ManagementPacks.GetManagementPack(new Guid("545131F0-58DE-1914-3A82-4FCAC9100A33")); //Microsoft.Windows.Library
      ManagementPack mpSystemWorkItemActivityLibrary = emg.ManagementPacks.GetManagementPack(new Guid("AA265D90-3E2E-B9A2-D929-BE0D36F1A53E")); //System.WorkItem.Activity.Library
    
      //Get management pack classes. You could just use the GUID's if you know them.
      ManagementPackClass mpcReviewActivity = emg.EntityTypes.GetClass("System.WorkItem.Activity.ReviewActivity", mpSystemWorkItemActivityLibrary);
      ManagementPackClass mpcADUser = emg.EntityTypes.GetClass("Microsoft.AD.User", mpMicrosoftWindowsLibrary);
      ManagementPackClass mpcReviewer = emg.EntityTypes.GetClass("System.Reviewer", mpSystemWorkItemActivityLibrary);
    
      //The relationships we'll need to relate our reviewer to an AD User and the review activity.
      ManagementPackRelationship mprReviewerIsUser = emg.EntityTypes.GetRelationshipClass("System.ReviewerIsUser", mpSystemWorkItemActivityLibrary);
      ManagementPackRelationship mprWorkItemContainsActivity = emg.EntityTypes.GetRelationshipClass("System.WorkItemContainsActivity", mpSystemWorkItemActivityLibrary);
      ManagementPackRelationship mprReviewActivityHasReviewer = emg.EntityTypes.GetRelationshipClass("System.ReviewActivityHasReviewer", mpSystemWorkItemActivityLibrary);
    
      //Here's the simple criteria that will let you get the AD User object from the SM CMDB
      string strADUserCriteria = String.Format(@"
      <Criteria xmlns=""http://Microsoft.EnterpriseManagement.Core.Criteria/"">
       <Reference Id=""Microsoft.Windows.Library"" PublicKeyToken=""{0}"" Version=""{1}"" Alias=""MSWinLib"" />
       <Expression>
       <SimpleExpression>
        <ValueExpressionLeft>
        <Property>$Target/Property[Type='MSWinLib!Microsoft.AD.User']/UserName$</Property>
        </ValueExpressionLeft>
        <Operator>Equal</Operator>
        <ValueExpressionRight>
        <Value>" + strADUserName + @"</Value>
        </ValueExpressionRight>
       </SimpleExpression>
       </Expression>
      </Criteria>
      ", mpMicrosoftWindowsLibrary.KeyToken, mpMicrosoftWindowsLibrary.Version.ToString());
    
      //Get the AD User
      EnterpriseManagementObjectCriteria emocADUser = new EnterpriseManagementObjectCriteria(strADUserCriteria, mpcADUser, emg);
      IObjectReader<EnterpriseManagementObject> orADUser = emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(emocADUser, objQueryOpts);
      //Since UserName is a key, this query should only ever return 1 result unless you have the same username on multiple domains.
      EnterpriseManagementObject emoADUser = orADUser.ElementAt(0); 
    
      //Now we create our reviewer. This is a projection because we'll need to relate this reviewer to a user and activity
      EnterpriseManagementObjectProjection emopReviewerToCreate = new EnterpriseManagementObjectProjection(emg, mpcReviewer);
      //This is up to you whether the user must vote or not. You can also set other properties here if you wish.
      emopReviewerToCreate.Object[mpcReviewer, "MustVote"].Value = true; 
      //Relate the recently found AD User to this new Reviewer.
      emopReviewerToCreate.Add(emoADUser, mprReviewerIsUser.Target);
    
      //Remember the new Change Request you're creating? Even though it hasn't been created yet, you can
      //loop through it's relations! This means you can find the review activity that's part of your template.
      foreach (IComposableProjection icpActivity in emopNewCR[mprWorkItemContainsActivity.Target])
      {
      //Since it's very likely that you have multiple activities in your template, you'll have to find the specific
      //review activity you wish to update. This code will update EVERY review activity. You can add additional contraints
      //here if you wish.
      //Notice that all we're doing here is comparing this activity to a review activity class object. It's a handy way
      //of verifying that whatever object you're looking at matches a class you're interested in. There's a slightly
      //shorter way of doing it, but this is how I've always done it :)
      if (emg.EntityTypes.GetClass(icpActivity.Object.LeastDerivedNonAbstractManagementPackClassId) == mpcReviewActivity)
      {
       //We've now found the review activity. Relate our new Reviewer to it. A nifty part is that
       //the reviewer doesn't have to exist yet! You can relate a pre-built object as easily as an existing object!
       icpActivity.Add(emopReviewerToCreate, mprReviewActivityHasReviewer.Target);
      }
      }
    
      //This builds the whole 9 yards. It creates your Change Request, the related activity, the reviewer
      //and creates all the relationships in one shot.
      emopNewCR.Overwrite();
    
    • Marked as answer by DashKappei Sunday, January 09, 2011 12:21 PM
    Friday, January 07, 2011 11:49 PM
  • Wow :) It works perfectly thanks! :D

    I had to modify my code to fit yours, but it was quite simple :)

     

    Now I had two final questions to complete my scenario, but it's of course necessary to describe my current configuration:

    1. Sealed MP that contains my CR template, a custom Activity class (used to implement an automated activity), a custom Workflow (used to automatically add an AD user to an AD group) and an Activity template based on the custom Activity class.
    2. The CR template has two activities: review activity (without reviewer) and automated activity (based on the Activity template)
    3. My custom Activity is based on Activity class and has two custom string attributes: ADUserName and ADGroupName
    4. I need to populate such attributes with the ones that are currently available by the aspx code
    5. I need to populate AssignedTo and CreatedBy relationships on the Change class

    To achieve this goals I tried to extend my current aspx page, but...Even though the page doesn't notify errors at all (I mean: the Change Request is properly created), such attributes and relationships are still blank.

    Here's my code :)

    string smServer = "demosm1.demolab.lan"; 
    Guid guidTemplateCR = new Guid("3C58918C-D6D4-D84F-9724-2EC81923E6D3"); //CR Template GUID
    
    ObjectQueryOptions objQueryOpts = new ObjectQueryOptions();
    objQueryOpts.ObjectRetrievalMode = ObjectRetrievalOptions.Buffered;
    objQueryOpts.DefaultPropertyRetrievalBehavior = ObjectPropertyRetrievalBehavior.All;
    objQueryOpts.MaxResultCount = 250;
    EnterpriseManagementGroup emg = new EnterpriseManagementGroup(smServer);
    			
    ManagementPack systemMp = emg.ManagementPacks.GetManagementPack(SystemManagementPack.System);
    ManagementPack mpMicrosoftWindowsLibrary = emg.ManagementPacks.GetManagementPack(new Guid("545131F0-58DE-1914-3A82-4FCAC9100A33")); //Microsoft.Windows.Library
    ManagementPack mpSystemWorkItemActivityLibrary = emg.ManagementPacks.GetManagementPack(new Guid("AA265D90-3E2E-B9A2-D929-BE0D36F1A53E")); //System.WorkItem.Activity.Library
    ManagementPack mpSystemWorkItemLibrary = emg.ManagementPacks.GetManagementPack(new Guid("405D5590-B45F-1C97-024F-24338290453E"));  //System.WorkItem.Library
    ManagementPack mpAutomatedActivityLibrary = emg.ManagementPacks.GetManagementPack(new Guid("9A63320D-426C-737B-9E9A-FB2519F289CC")); //My Custom MP Library
    ManagementPack changeConfigurationMp = emg.ManagementPacks.GetManagementPack("ServiceManager.ChangeManagement.Configuration", null, systemMp.Version);
    ManagementPack changeManagementMp = emg.ManagementPacks.GetManagementPack("System.WorkItem.ChangeRequest.Library", systemMp.KeyToken, systemMp.Version);
    
    ManagementPackClass mpcReviewActivity = emg.EntityTypes.GetClass("System.WorkItem.Activity.ReviewActivity", mpSystemWorkItemActivityLibrary);
    ManagementPackClass mpcAutomatedActivity = emg.EntityTypes.GetClass("My.Custom.Automated.Activity", mpAutomatedActivityLibrary);
    ManagementPackClass mpcADUser = emg.EntityTypes.GetClass("Microsoft.AD.User", mpMicrosoftWindowsLibrary);
    ManagementPackClass mpcReviewer = emg.EntityTypes.GetClass("System.Reviewer", mpSystemWorkItemActivityLibrary);
    ManagementPackClass changeClass = emg.EntityTypes.GetClass("System.WorkItem.ChangeRequest", changeManagementMp);
    
    ManagementPackObjectTemplate template = emg.Templates.GetObjectTemplate(guidTemplateCR);
     
    ManagementPackRelationship mprReviewerIsUser = emg.EntityTypes.GetRelationshipClass("System.ReviewerIsUser", mpSystemWorkItemActivityLibrary);
    ManagementPackRelationship mprWorkItemContainsActivity = emg.EntityTypes.GetRelationshipClass("System.WorkItemContainsActivity", mpSystemWorkItemActivityLibrary);
    ManagementPackRelationship mprReviewActivityHasReviewer = emg.EntityTypes.GetRelationshipClass("System.ReviewActivityHasReviewer", mpSystemWorkItemActivityLibrary);
    ManagementPackRelationship mprAssignedToUser = emg.EntityTypes.GetRelationshipClass("System.WorkItemAssignedToUser", mpSystemWorkItemLibrary); //Is this correct? :)
    ManagementPackRelationship mprCreatedByUser = emg.EntityTypes.GetRelationshipClass("System.WorkItemCreatedByUser", mpSystemWorkItemLibrary); //Is this correct? :)
    			
    EnterpriseManagementObjectProjection emopNewCR = new EnterpriseManagementObjectProjection(emg, changeClass);
    			
    emopNewCR.ApplyTemplate(template);
    emopNewCR.Object[changeClass, "Id"].Value = "CR{0}";
    emopNewCR.Object[changeClass, "Title"].Value = "My custom CR title"; 
    emopNewCR.Object[changeClass, "Description"].Value = "My custom CR description"; 
    emopNewCR.Object[changeClass, "Reason"].Value = "My custom CR reason"; 
    emopNewCR.Object[changeClass, "DisplayName"].Value = emopNewCR.Object[changeClass, "Id"].Value + ": My custom Service Request"; 
    emopNewCR.Object[changeClass, "Notes"].Value = ADGroup; //Selected through a drop-down list
    
    string strADUserCriteria1 = CreateUserCriteriaXml(mpMicrosoftWindowsLibrary, requestor); //requestor is the IE-user
    EnterpriseManagementObjectCriteria emocADUser1 = new EnterpriseManagementObjectCriteria(strADUserCriteria1, mpcADUser, emg);
    IObjectReader<EnterpriseManagementObject> orADUser1 = emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(emocADUser1, objQueryOpts);
    EnterpriseManagementObject emoADUser1 = orADUser1.ElementAt(0); 
    
    string strADUserCriteria2 = CreateUserCriteriaXml(mpMicrosoftWindowsLibrary, apprVal); //apprVal is the user assigned to the change as well as the reviewer
    EnterpriseManagementObjectCriteria emocADUser2 = new EnterpriseManagementObjectCriteria(strADUserCriteria2, mpcADUser, emg);
    IObjectReader<EnterpriseManagementObject> orADUser2 = emg.EntityObjects.GetObjectReader<EnterpriseManagementObject>(emocADUser2, objQueryOpts);
    EnterpriseManagementObject emoADUser2 = orADUser2.ElementAt(0);
    
    //Are the following 4 lines correct? :)
    EnterpriseManagementObjectProjection emopChangeOwner = new EnterpriseManagementObjectProjection(emg, changeClass);
    emopChangeOwner.Add(emoADUser1, mprAssignedToUser.Target);
    EnterpriseManagementObjectProjection emopCreatedBy = new EnterpriseManagementObjectProjection(emg, changeClass);
    emopCreatedBy.Add(emoADUser2, mprCreatedByUser.Target);			
     
    EnterpriseManagementObjectProjection emopReviewerToCreate = new EnterpriseManagementObjectProjection(emg, mpcReviewer);
    emopReviewerToCreate.Object[mpcReviewer, "MustVote"].Value = true;
    emopReviewerToCreate.Add(emoADUser2, mprReviewerIsUser.Target);
    
    //Are the following 3 lines correct? :)
    EnterpriseManagementObjectProjection emopAutomatedActivity = new EnterpriseManagementObjectProjection(emg, mpcAutomatedActivity);
    emopAutomatedActivity.Object[mpcAutomatedActivity, "ADGroupName"].Value = ADGroup;
    emopAutomatedActivity.Object[mpcAutomatedActivity, "ADUserName"].Value = IEUser;
     			
     			
    foreach (IComposableProjection icpActivity in emopNewCR[mprWorkItemContainsActivity.Target])
    {
    	if (emg.EntityTypes.GetClass(icpActivity.Object.LeastDerivedNonAbstractManagementPackClassId) == mpcReviewActivity)
    	{
    		icpActivity.Add(emopReviewerToCreate, mprReviewActivityHasReviewer.Target);
    	}
    }
     			
    emopNewCR.Overwrite();
    

     

    Thanks for any help :) 

    Sunday, January 09, 2011 1:04 PM
  • To your questions about whether the lines are correct: you're close. The two relationship classes look fine.

    The 4 lines where you're creating an emopChangeOwner, you don't want to create a new projection, you want to relate your emoADUser1/2 directly to your emopNewCR by doing:

    emopNewCR.Add(emoADUser1,mprAssignedToUser.Target);

    Same for CreatedBy

    emopNewCR.Add(emoADUser2,mprCreatedByUser.Target);

    Lastly where you're creating your new Automated Activity, don't forget to relate it to your new CR

    emopNewCR.Add(emopAutomatedActivity,mprWorkItemContainsActivity.Target);

     

    I haven't tried this code, but it should work. I've personally never programatically added activities, so I don't know in what order they're added (probably in-order).

    Tuesday, January 11, 2011 7:29 PM
  • HI Aaron,

    Great post with great insights. BTW, does this approach work for creating a new Service Request based on a Request Offering? How do I achieve it programmatically? What I'm looking at is to create a new SR for the "Cloud Services Create new VM Request offering".

    And I've not been able to somehow get the concept as to how to relate a plain SR object with the Request Offering object.

    Any help would be much appreciated.

    Thanks.

    Saturday, March 31, 2012 2:04 AM
  •  I've personally never programatically added activities, so I don't know in what order they're added (probably in-order).


    Any activity has a "SequenceId" property what assign order in CR.

    http://www.scsmsolutions.com/ freemanru (at) gmail (dot) com


    Monday, April 02, 2012 10:14 AM
    Moderator