Sample: Update next birthday using a custom workflow activity

 

Applies To: Dynamics CRM 2015

This sample code is for Microsoft Dynamics CRM 2015 and Microsoft Dynamics CRM Online 2015 Update. Download the Microsoft Dynamics CRM SDK package. It can be found in the following location in the download package:

SampleCode\CS\Process\CustomWorkflowActivity\TestNet4Activity\ReleaseISVActivities.cs

Requirements

The following custom field must exist for this custom workflow activity to work:

  • Contact.new_nextbirthday

Demonstrates

The following sample workflow activity returns the next birthday. Use this in a workflow that sends a birthday greeting to a customer.

Example


using System;
using System.Activities;
using System.Collections;
using System.Reflection;

using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;

namespace Microsoft.Crm.Sdk.Samples
{
    /// <summary>
    /// Activity will return the next upcoming birthday that has just passed
    /// 
    /// If this year's birthday has not yet occurred, it will return this year's birthday
    /// Otherwise, it will return the birthday for next year
    /// 
    /// A workflow can timeout when on this date
    /// </summary>
    [Persist]
    public sealed partial class ReleaseScenario_UpdateNextBirthday : CodeActivity
    {
        protected override void Execute(CodeActivityContext executionContext)
        {
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            //Create an Organization Service
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.InitiatingUserId);

            //Retrieve the contact id
            Guid contactId = this.Contact.Get(executionContext).Id;

            //Create the request
            RetrieveRequest request = new RetrieveRequest();
            request.ColumnSet = new ColumnSet(new string[] { "birthdate" });
            request.Target = new EntityReference(EntityName.Contact, contactId);

            //Retrieve the entity to determine what the birthdate is set at
            Entity entity = (Entity)((RetrieveResponse)service.Execute(request)).Entity;

            //Extract the date out of the entity
            DateTime? birthdate;
            if (entity.Contains(ContactAttributes.Birthdate))
            {
                birthdate = (DateTime?)entity[ContactAttributes.Birthdate];
            }
            else
            {
                birthdate = null;
            }

            //Check to see if the current birthday is set. We don't want the activity to fail if the birthdate is not set
            if (birthdate == null)
            {
                return;
            }

            //Calculate the next birthdate. Encapsulated in a methdo so that the method can be used in the test case for verification purposes
            DateTime nextBirthdate = CalculateNextBirthday(birthdate.Value);

            //Update the next birthday field on the entity
            Entity updateEntity = new Entity(EntityName.Contact);
            updateEntity.Id = contactId;
            updateEntity["new_nextbirthday"] = nextBirthdate;

            service.Update(updateEntity);
        }

        //Define the properties
        [RequiredArgument]
        [Input("Update Next Birthdate for")]
        [ReferenceTarget("contact")]
        public InArgument<EntityReference> Contact { get; set; }

        private DateTime CalculateNextBirthday(DateTime birthdate)
        {
            DateTime nextBirthday = new DateTime(birthdate.Year, birthdate.Month, birthdate.Day);

            //Check to see if this birthday occurred on a leap year
            bool leapYearAdjust = false;
            if (nextBirthday.Month == 2 &amp;&amp; nextBirthday.Day == 29)
            {
                //Sanity check, was that year a leap year
                if (DateTime.IsLeapYear(nextBirthday.Year))
                {
                    //Check to see if the current year is a leap year
                    if (!DateTime.IsLeapYear(DateTime.Now.Year))
                    {
                        //Push the date to March 1st so that the date arithmetic will function correctly
                        nextBirthday = nextBirthday.AddDays(1);
                        leapYearAdjust = true;
                    }
                }
                else
                {
                    throw new Exception("Invalid Birthdate specified", new ArgumentException("Birthdate"));
                }
            }

            //Calculate the year difference
            nextBirthday = nextBirthday.AddYears(DateTime.Now.Year - nextBirthday.Year);

            //Check to see if the date was adjusted
            if (leapYearAdjust &amp;&amp; DateTime.IsLeapYear(nextBirthday.Year))
            {
                nextBirthday = nextBirthday.AddDays(-1);
            }

            return nextBirthday;
        }
    }

    public sealed partial class ReleaseScenario_MessageName : CodeActivity
    {
        protected override void Execute(CodeActivityContext executionContext)
        {
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            //Set the variable
            MessageName.Set(executionContext, context.MessageName);
        }

        //Define the properties
        [Output("Message Name")]
        public OutArgument<string> MessageName { get; set; }
    }

    /// <summary>
    /// Calculates the credit score based on the SSN and name. 
    /// </summary>
    /// <remarks>
    /// This depends on a custom entity called Loan Application and customizations to Contact.
    /// 
    /// Loan Application requires the following properties:
    /// <list>
    ///     <item>Schema Name: new_loanapplication</item>
    ///     <item>Attribute: new_loanapplicationid as the primary key</item>
    ///     <item>Attribute: new_creditscore of type int with min of 0 and max of 1000 (if it is to be updated)</item>
    ///     <item>Attribute: new_loanamount of type money with default min/max</item>
    ///     <item>Customize the form to include the attribute new_loanapplicantid</item>
    /// </list>
    /// 
    /// Contact Requires the following customizations
    /// <list>
    ///     <item>Attribute: new_ssn as nvarchar with max length of 15</item>
    ///     <item>One-To-Many Relationship with these properties:
    ///         <list>
    ///             <item>Relationship Definition Schema Name: new_loanapplicant</item>
    ///             <item>Relationship Definition Related Entity Name: Loan Application</item>
    ///             <item>Relationship Atttribute Schema Name: new_loanapplicantid</item>
    ///             <item>Relationship Behavior Type: Referential</item>
    ///         </list>
    ///     </item>
    /// </list>
    /// 
    /// The activity takes, as input, a EntityReference to the Loan Application and a boolean indicating whether new_creditscore should be updated to the credit score.
    /// </remarks>
    /// <exception cref=">ArgumentNullException">Thrown when LoanApplication is null</exception>
    /// <exception cref=">ArgumentException">Thrown when LoanApplication is not a EntityReference to a LoanApplication entity</exception>
    public sealed partial class RetrieveCreditScore : CodeActivity
    {
        private const string CustomEntity = "new_loanapplication";

        protected override void Execute(CodeActivityContext executionContext)
        {
            //Check to see if the EntityReference has been set
            EntityReference loanApplication = this.LoanApplication.Get(executionContext);
            if (loanApplication == null)
            {
                throw new InvalidOperationException("Loan Application has not been specified", new ArgumentNullException("Loan Application"));
            }
            else if (loanApplication.LogicalName != CustomEntity)
            {
                throw new InvalidOperationException("Loan Application must reference a Loan Application entity",
                    new ArgumentException("Loan Application must be of type Loan Application", "Loan Application"));
            }

            //Retrieve the CrmService so that we can retrieve the loan application
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.InitiatingUserId);

            //Retrieve the Loan Application Entity
            Entity loanEntity;
            {
                //Create a request
                RetrieveRequest retrieveRequest = new RetrieveRequest();
                retrieveRequest.ColumnSet = new ColumnSet(new string[] { "new_loanapplicationid", "new_loanapplicantid" });
                retrieveRequest.Target = loanApplication;

                //Execute the request
                RetrieveResponse retrieveResponse = (RetrieveResponse)service.Execute(retrieveRequest);

                //Retrieve the Loan Application Entity
                loanEntity = retrieveResponse.Entity as Entity;
            }

            //Retrieve the Contact Entity
            Entity contactEntity;
            {
                //Create a request
                EntityReference loanApplicantId = (EntityReference)loanEntity["new_loanapplicantid"];

                RetrieveRequest retrieveRequest = new RetrieveRequest();
                retrieveRequest.ColumnSet = new ColumnSet(new string[] { "fullname", "new_ssn", "birthdate" });
                retrieveRequest.Target = loanApplicantId;

                //Execute the request
                RetrieveResponse retrieveResponse = (RetrieveResponse)service.Execute(retrieveRequest);

                //Retrieve the Loan Application Entity
                contactEntity = retrieveResponse.Entity as Entity;
            }

            //Retrieve the needed properties
            string ssn = (string)contactEntity["new_ssn"];
            string name = (string)contactEntity[ContactAttributes.FullName];
            DateTime? birthdate = (DateTime?)contactEntity[ContactAttributes.Birthdate];
            int creditScore;

            //This is where the logic for retrieving the credit score would be inserted
            //We are simply going to return a random number
            creditScore = (new Random()).Next(0, 1000);

            //Set the credit score property
            this.CreditScore.Set(executionContext, creditScore);

            //Check to see if the credit score should be saved to the entity
            //If the value of the property has not been set or it is set to true
            if (null != this.UpdateEntity &amp;&amp; this.UpdateEntity.Get(executionContext))
            {
                //Create the entity
                Entity updateEntity = new Entity(loanApplication.LogicalName);
                updateEntity["new_loanapplicationid"] = loanEntity["new_loanapplicationid"];
                updateEntity["new_creditscore"] = this.CreditScore.Get(executionContext);

                //Update the entity
                service.Update(updateEntity);
            }
        }

        //Define the properties
        [Input("Loan Application")]
        [ReferenceTarget(CustomEntity)]
        public InArgument<EntityReference> LoanApplication { get; set; }

        [Input("Update Entity's Credit Score")]
        [Default("true")]
        public InArgument<bool> UpdateEntity { get; set; }

        [Output("Credit Score")]
        [AttributeTarget(CustomEntity, "new_creditscore")]
        public OutArgument<int> CreditScore { get; set; }
    }
}

See Also

IWorkflowContext
Custom workflow activities (workflow assemblies)
Sample: Calculate a credit score with a custom workflow activity
Add metadata to a custom workflow activity

© 2016 Microsoft. All rights reserved. Copyright