Sample: Update next birthday using a custom workflow activity
Applies To: Dynamics CRM 2013
This sample code is for Microsoft Dynamics CRM 2013 and Microsoft Dynamics CRM Online. 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 && 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 && 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 && 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