Quantcast
Channel: Thinking in MS CRM
Viewing all articles
Browse latest Browse all 11

Retrieve connections from a record, filter them on the name of the connection role and send email (CRM 2011)

$
0
0
We have had the need of retrieve all the records connected to a given record with a specific connection role, and send an email to the corresponding email addresses. This "notification" must occur in response of a "field change" event on the form. We developed one custom workflow activity to implement this requisite.
One complexity to consider is on the generalization of the records type: we can have contacts, as well as accounts, system users and so on.
An important consideration concerns the name of the connection role: there is no unique constraint on it in Dynamics CRM 2011, therefore there could be two different connection roles with the same name. If this happens, and there are two connections to a record with "different" connection roles (but same name), the "magic" behind CRM retrieves both of them.
Another important point is the need to skip (in some way) the retrieved (and filtered) records that have not a value for the email. This is because the asynchronous nature of the workflow. During the retrieving records, if a column (in the column set) has no value, it results in the "absence" of the corresponding attribute in the retrieved record. When the workflow tries to read it, the system puts it in "Waiting status" in the wait for a value (with a finite number of tentatives). This is not what we want, because logically, if a record has no email, simply we want to avoid to notify via email.
One important point to focus in the code below is the parameter (InArgument<EntityReference>) "EmailMessage".
This implementative choice is because we need all the features provided natively (OOB) from email message (Rich Text Box in the body, Activity Party for the "From" and "To" field, and so on), that we would lose if creation of the email was inside our CWA.
We implemented this "pattern" to pass the email created as parameter, and afterwards the CWA processes it adding the recipients (To) retrieved with our custom logic.

using System;

using System.Activities;

using System.ServiceModel;

using Microsoft.Xrm.Sdk;

using Microsoft.Xrm.Sdk.Workflow;

using System.Linq;

using Microsoft.Crm.Sdk.Messages;

using System.Collections.Generic;

using Microsoft.Xrm.Sdk.Query;


publicsealedclassRetrieveConnectionsByRolesAndProcessEmail : CodeActivity

{

    //Email message

    [RequiredArgument]

    [Input("Email")]

    [ReferenceTarget("email")]

    publicInArgument<EntityReference> EmailMessage

    {

        get;

        set;

    }


    [Input("Role To (Name)")]

    publicInArgument<string> RoleToName

    {

        get;

        set;

    }


    [Input("Role From (Name)")]

    publicInArgument<string> RoleFromName

    {

        get;

        set;

    }


    ///<summary>

    /// Executes the workflow activity.

    ///</summary>

    ///<param name="executionContext">The execution context.</param>

    protectedoverridevoid Execute(CodeActivityContext executionContext)

    {

        // Create the tracing service

        ITracingService tracingService = executionContext.GetExtension<ITracingService>();


        if (tracingService == null)

        {

            thrownewInvalidPluginExecutionException("Failed to retrieve tracing service.");

        }


        tracingService.Trace("Entered RetrieveConnectionsByRolesAndProcessEmail.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",

            executionContext.ActivityInstanceId,

            executionContext.WorkflowInstanceId);


        // Create the context

        IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();


        if (context == null)

        {

            thrownewInvalidPluginExecutionException("Failed to retrieve workflow context.");

        }


        tracingService.Trace("RetrieveConnectionsByRolesAndProcessEmail.Execute(), Correlation Id: {0}, Initiating User: {1}",

            context.CorrelationId,

            context.InitiatingUserId);


        IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();

        IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);


        //Retrieve Pre Image entity

        Entity preImageEntity = context.PreEntityImages.Values.FirstOrDefault();


        try

        {

            List<ActivityParty> emailAddresses = newList<ActivityParty>();

            //string emailAddresses = string.Empty;


            // This query retrieves all connections this record is part of.

            QueryExpression query = newQueryExpression

            {

                EntityName = Connection.EntityLogicalName,

                ColumnSet = newColumnSet("connectionid", "record2id", "record1roleid", "record2roleid"), //Connection ID, Connected To, As this role (From), As this role (To)

                Criteria = newFilterExpression

                {

                    FilterOperator = LogicalOperator.And,

                    Conditions =

                {

                    newConditionExpression

                    {

                        AttributeName = "record1id",

                        Operator = ConditionOperator.Equal,

                        Values = { preImageEntity.Id }

                    }

                }

                }

            };


            //Filter on Role To if provided

            if (!string.IsNullOrEmpty(this.RoleToName.Get(executionContext).ToString()))

            {

                //Check if the role´s name is correct

                QueryExpression queryRoleTo = newQueryExpression

                {

                    EntityName = ConnectionRole.EntityLogicalName,

                    ColumnSet = newColumnSet(false),

                    Criteria = newFilterExpression

                    {

                        FilterOperator = LogicalOperator.And,

                        Conditions =

                    {

                        newConditionExpression

                        {

                            AttributeName = "name",

                            Operator = ConditionOperator.Equal,

                            Values = { this.RoleToName.Get(executionContext).ToString() }

                        }

                    }

                    }

                };


                var rolesTo = service.RetrieveMultiple(queryRoleTo);

                if (rolesTo.Entities.Count() == 0)

                {

                    thrownewInvalidPluginExecutionException("No connection role with the name provided: please check the \"Role From\" and correct it");

                }


                LinkEntity linkEntityRoleTo = newLinkEntity();

                linkEntityRoleTo.JoinOperator = JoinOperator.Natural;

                linkEntityRoleTo.LinkFromEntityName = Connection.EntityLogicalName;

                linkEntityRoleTo.LinkFromAttributeName = "record2roleid";

                linkEntityRoleTo.LinkToEntityName = ConnectionRole.EntityLogicalName;

                linkEntityRoleTo.LinkToAttributeName = "connectionroleid";



                ConditionExpression conditionRoleTo = newConditionExpression(

                    "name",

                    ConditionOperator.Equal,

                    newobject[] { this.RoleToName.Get(executionContext).ToString() }

                );


                linkEntityRoleTo.LinkCriteria.Conditions.AddRange(newConditionExpression[] { conditionRoleTo });


                query.LinkEntities.AddRange(newLinkEntity[] { linkEntityRoleTo });

            }


            //Filter on Role From if provided

            if (!string.IsNullOrEmpty(this.RoleFromName.Get(executionContext).ToString()))

            {

                //Check if the role´s name is correct

                QueryExpression queryRoleFrom = newQueryExpression

                {

                    EntityName = ConnectionRole.EntityLogicalName,

                    ColumnSet = newColumnSet(false),

                    Criteria = newFilterExpression

                    {

                        FilterOperator = LogicalOperator.And,

                        Conditions =

                    {

                        newConditionExpression

                        {

                            AttributeName = "name",

                            Operator = ConditionOperator.Equal,

                            Values = { this.RoleFromName.Get(executionContext).ToString() }

                        }

                    }

                    }

                };


                var rolesFrom = service.RetrieveMultiple(queryRoleFrom);

                if (rolesFrom.Entities.Count() == 0)

                {

                    thrownewInvalidPluginExecutionException("No connection role with the name provided: please check the \"Role To\" and correct it");

                }


                LinkEntity linkEntityRoleFrom = newLinkEntity();

                linkEntityRoleFrom.JoinOperator = JoinOperator.Natural;

                linkEntityRoleFrom.LinkFromEntityName = Connection.EntityLogicalName;

                linkEntityRoleFrom.LinkFromAttributeName = "record1roleid";

                linkEntityRoleFrom.LinkToEntityName = ConnectionRole.EntityLogicalName;

                linkEntityRoleFrom.LinkToAttributeName = "connectionroleid";



                ConditionExpression conditionRoleFrom = newConditionExpression(

                    "name",

                    ConditionOperator.Equal,

                    newobject[] { this.RoleFromName.Get(executionContext).ToString() }

                );


                linkEntityRoleFrom.LinkCriteria.Conditions.AddRange(newConditionExpression[] { conditionRoleFrom });


                query.LinkEntities.AddRange(newLinkEntity[] { linkEntityRoleFrom });

            }


            EntityCollection results = service.RetrieveMultiple(query);


            string entityName, emailFieldName = string.Empty;

            ColumnSet columnSet = newColumnSet();


            if (results.Entities.Count() == 0)

                return;


            foreach (Entity connection in results.Entities.AsEnumerable())

            {

                entityName = connection.Attributes.Contains("record2id") ? ((EntityReference)connection.Attributes["record2id"]).LogicalName : string.Empty;


                //The list below contains all and only the entity allowed in the "Email To" field

                //Every other entity record type is ignored and not managedLoop

                switch (entityName)

                {

                    caseSystemUser.EntityLogicalName:

                        columnSet = newColumnSet(newstring[] { emailFieldName = "internalemailaddress" });

                        break;

                    caseAccount.EntityLogicalName:

                        columnSet = newColumnSet(newstring[] { emailFieldName = "emailaddress1" });

                        break;

                    caseContact.EntityLogicalName:

                        columnSet = newColumnSet(newstring[] { emailFieldName = "emailaddress1" });

                        break;

                    caseQueue.EntityLogicalName:

                        columnSet = newColumnSet(newstring[] { emailFieldName = "emailaddress" });

                        break;

                    caseLead.EntityLogicalName:

                        columnSet = newColumnSet(newstring[] { emailFieldName = "emailaddress1" });

                        break;

                    caseEquipment.EntityLogicalName:

                        columnSet = newColumnSet(newstring[] { emailFieldName = "emailaddress" });

                        break;

                    default:

                        emailFieldName = string.Empty;

                        break;

                }


                Entity record;


                //If entity record not valid --> skip

                if (!string.IsNullOrEmpty(emailFieldName))

                {

                    //Retrieving only one field (email address) imply less complexity and "maximize" query-performances

                    record = service.Retrieve(entityName, ((EntityReference)connection.Attributes["record2id"]).Id, columnSet);

                    

                    if (record.Attributes.Contains(emailFieldName)) //If email address is not set --> skip

                    {

                        emailAddresses.Add(newActivityParty() { PartyId = newEntityReference(entityName, record.Id), AddressUsed = record.Attributes[emailFieldName].ToString() });

                    }

                }

            }


            //Retrieving email (Email parameter is required)

            Email email = (Email)service.Retrieve(Email.EntityLogicalName, EmailMessage.Get(executionContext).Id, newColumnSet(false));


            //Set "To" (Recipients)

            email.To = emailAddresses;


            //Update email

            service.Update(email);


            //Send email

            SendEmailRequest sendEmailreq = newSendEmailRequest

            {

                EmailId = email.Id,

                TrackingToken = "",

                IssueSend = true

            };


            SendEmailResponse sendEmailresp = (SendEmailResponse)service.Execute(sendEmailreq);

        }

        catch (FaultException<OrganizationServiceFault> e)

        {

            tracingService.Trace("Exception: {0}", e.ToString());


            thrownewInvalidPluginExecutionException("Error message: " + e.Message + " - Error stacktrace: " + e.StackTrace);

        }


        tracingService.Trace("Exiting RetrieveConnectionsByRolesAndProcessEmail.Execute(), Correlation Id: {0}", context.CorrelationId);

    }

}



Hope it can be useful!
Happy CRM coding!

Viewing all articles
Browse latest Browse all 11

Latest Images

Trending Articles





Latest Images