Custom Workflow Activity to copy notes

Answered Custom Workflow Activity to copy notes

  • Thursday, May 31, 2012 8:18 PM
     
      Has Code

    I'm trying to create a generic workflow activity which will copy notes from one object to another, regardless of the entity type. Being new to this, I'm struggling to get it working.

    The logic of my code is to accept an object as an input (CopyFrom). This is then queried to retrieve all the notes associated with it. For each note found, a new one is created and associated with the second object input property (CopyTo).

    I'd appreciate it if someone can review my code and see if they can see any issues or if I'm going about this the wrong way.

        public class CopyNotes : CodeActivity
        {
            protected override void Execute(CodeActivityContext executionContext)
            {
                //Create the tracing service
                ITracingService tracingService = executionContext.GetExtension<ITracingService>();
    
                //Create the context
                tracingService.Trace("Getting the context.");
                IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
                IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
                IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
    
                //get the notes associated with the source entity
                Guid copyFromId = CopyFrom.Get(executionContext).Id;
                Guid copyToId = CopyTo.Get(executionContext).Id;
    
                tracingService.Trace(string.Format("Retrieving the notes for the Copy From entity: {0}.", copyFromId.ToString()));
    
                EntityCollection copyFromNotes = RetrieveNotes(service, copyFromId);
    
                if (copyFromNotes.Entities.Any())
                {
                    tracingService.Trace(string.Format("Entity {0} has notes.", copyFromId.ToString()));
                    foreach (Entity e in copyFromNotes.Entities)
                    {
                        Entity newNote = new Entity("annotation");
                        newNote.Attributes["subject"] = e.Attributes["subject"];
                        newNote.Attributes["notetext"] = e.Attributes["notetext"];
                        newNote.Attributes["objectid"] = copyToId.ToString();
                    }
                }
                else
                {
                    tracingService.Trace(string.Format("Entity {0} doesn't have any notes.", copyToId.ToString()));
                }
            }
    
            private EntityCollection RetrieveNotes(IOrganizationService service, Guid relatedObject)
            {
                ConditionExpression condition = new ConditionExpression();
                condition.AttributeName = "objectid";
                condition.Operator = ConditionOperator.Equal;
                condition.Values.Add(relatedObject.ToString());
    
                ColumnSet columns = new ColumnSet("subject", "notetext");
    
                QueryExpression query = new QueryExpression();
                query.ColumnSet = columns;
                query.EntityName = "annotation";
                query.Criteria.AddCondition(condition);
    
                EntityCollection results = service.RetrieveMultiple(query);
    
                return results;
    
            }
    
            [RequiredArgument]
            [ReferenceTarget("task")]
            [Input("Copy notes from item")]
            public InArgument<EntityReference> CopyFrom { get; set; }
    
            [RequiredArgument]
            [ReferenceTarget("incident")]
            [Input("Copy notes to item")]
            public InArgument<EntityReference> CopyTo { get; set; }
        }

    One area I'm particularly having a problem with is that the InArgument class requires the ReferenceTarget to be set when I use the EntityReference setting. This defeats the purpose of me trying make this able to handle any entity.

All Replies

  • Thursday, May 31, 2012 9:43 PM
     
      Has Code

    This field:

    newNote.Attributes["objectid"]

    is an EntityReference, not Guid, nor string. You can create an entityreference : new EntityReference(){ Id = guid, LogicalName = "entityname" };, or you can just get it from an existing Entity object: object.ToEntityReference(). I suppose in this case, you can get the entity name this way:

    CopyTo.Get(executionContext).LogicalName

    Hope this helps.

    I can't help you with InArgument class problem, I haven't created a workflow acitivity :) yet.




  • Thursday, May 31, 2012 10:25 PM
     
     

    Thanks, I have updated that piece of code. There must be something up with my Input Parameters as I can't activate the process. When I try to, I get a generic error with no further details;

    This workflow has errors and cannot be published. Please remove the errors and try again.


  • Friday, June 01, 2012 3:38 AM
     
     

    In the above code Copy From is an input parameter that would need to be specified when the workflow is created along with the Copy to parameter.

    Another way to do this would be only accept one parameter for the CopyTo while read the details of the Copy From from the Context.Id which will provide the details of the record on which the workflow has been executed.

    As you have already found out, Input/Output param EntityReference require the name of the entity to be specified so I am not sure if you could make it generic to output to any object

    HTH

    Sam


    Dynamics CRM MVP | Inogic | http://inogic.blogspot.com| news at inogic dot com

    If this post answers your question, please click "Mark As Answer" on the post and "Mark as Helpful"

  • Friday, June 01, 2012 4:19 AM
     
     

    As a "workaround", you might try to use an "activitypointer" for the "copy to" parameter. Basically, since you can set activity's "regarding" to any non-activity entity, you could make your code generic in the following way:

    1. Create a new activity type(don't display that activity in the menus)
    2. Whenever you want to copy notes to a non-activity entity in your workflow, create an instance of that new activity and set "regarding" to the entity you want to copy notes to. Then, pass that new record to your custom workflow activity. 
    3. Also, whenever you want to copy notes to an "normal" activity entity, just pass that activity to your custom workflow activity directly
    4. In your code, use "copyto" to obtain activitypointer. Then, check the value of "activitytypecode" attribute. If it corresponds to the type created above(#1), then use "regardingobjectid" to copy notes. If activitytypecode is different, then use activityid to copy notes.



    Alex Shlega, GotchaHunter.Net


    • Edited by Alex Shlega Friday, June 01, 2012 4:20 AM
    •  
  • Friday, June 01, 2012 8:12 PM
     
     
    Whilst not being generic, my code above does nearly work. I forgot to do an IIS reset when I deployed it, hence why it wouldn't activate. It now activates and copies the note to the service case with one small issue. It copies the title of the task that the note is in and stores that as the note in the service case.
  • Saturday, June 02, 2012 2:46 PM
     
     Answered
    Turns out, I was just missing the line service.Create(newNote); after setting the attributes of the new note.
    • Marked As Answer by sparkymark75 Saturday, June 02, 2012 2:46 PM
    •