Category Archives: CRM2015 Update 1

How-To: Overcome Rollup Field Limitations with Rolling Batch Processing.. Even In the Cloud

Published by:

Rollup fields are great. But their filters are limited. The most common use case I can imagine is something like year to date sales. But you can’t do that with rollup fields because the filters don’t have relative dates. Want quarter-to-date sales? Sorry folks.

Here’s how to make it happen. This works.. even in the cloud. It should also work in 2011 although I have not tested it there.

In this scenario, we are using a Connection between a Product and an Account to hold quarter to dates sales numbers. I want to regularly calculate the sum of Invoices against the Account for the given Product and save it on the Connection. I’m calling the Connection my “target entity.”

Overcoming the Depth Property and Timeouts

Normally, you could create a recursive workflow that processes something, then calls itself. But you run into a problem with the Depth property; CRM will stop the process after so many iterations and consider it a runaway. So if your workflow calls itself, the 2nd time it runs the Depth property will be 2. After so many times, if Depth = x (I think 15 by default), CRM will kill the process.

The secret comes from Gonzalo Ruiz: the Depth property is cleared after 60 minutes.

The other issue is CRM’s timeouts; you need to make sure the update process doesn’t croak because it’s chugging through too many records.

So we’re going to chunk up our data into 1000 batches and run each batch asynchronously every 61 minutes. A lot of processes doing a little bit of work each. I don’t recommend this with synchronous processing.

The Approach

Here’s the process we’re going to create.

  1. Upon create, assign your target entity a random batch number. I’m using 1000 batches.
  2. An instance of a custom entity contains a batch number (1000 batch controller records for 1000 batches). A workflow fires on this custom entity record every 61 minutes.
  3. The workflow contains a custom workflow activity that updates all target records in its batch with a random number in a “trigger” field.
  4. A plugin against your target entity will listen for the trigger and fire the recalc.

2015-10-06 15_27_28-Drawing1 - Visio Professional

First, create a custom entity. I’ve called mine rh_rollingcalculationtrigger. All you need on it is one integer field.

Now, on your Connection (or whatever entity you want to store the rolling calculated fields), create two fields: rh_systemfieldrecalculationtrigger, and rh_systemfieldrecalculationbatch.

Now create a simple plugin to set the batch number to between 0-999 when the record is created. If you have existing records, you can export to Excel and reimport them with a random batch assignment – the Excel formula randbetween() is great for this.

protected void ExecutePostConnectionCreate(LocalPluginContext localContext)
{
	if (localContext == null)
	{
	throw new ArgumentNullException(localContext);
	}
	Random rand = new Random();
	IPluginExecutionContext context = localContext.PluginExecutionContext;
	Entity postImageEntity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
	ITracingService trace = localContext.TracingService;
	IOrganizationService service = localContext.OrganizationService;

	// super-simple update
	Entity newConnection = new Entity("connection");
	// Add a random number between 0 and 1000.
	newConnection["rh_systemfieldcalculationbatch"]= rand.Next(0, 1000);
	newConnection.Id = postImageEntity.Id;
	service.Update(newConnection);
}

(Side note: In C#, Random.Next() returns a value exclusive of the upper bound. So each record will get a value 0-999 inclusive.)

Now, we create a custom workflow activity. This inputs the batch number and fires a process called FireBatch. So when this workflow runs on a rh_rollingcalculationtrigger entity with Batch ID = 5, it will call FireBatch against all records with batch ID = 5.

In my case, I like to assemble the service, context and tracing service in the plugin and call my own class.

public sealed class WorkflowActivities : CodeActivity
    {
        /// <summary>
        /// Executes the workflow activity.
        /// </summary>
        /// <param name="executionContext">The execution context.</param>
        protected override void Execute(CodeActivityContext executionContext)
        {
            // Create the tracing service
            ITracingService tracingService = executionContext.GetExtension<ITracingService>();

            if (tracingService == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve tracing service.");
            }

            tracingService.Trace("Entered Class1.Execute(), Activity Instance Id: {0}, Workflow Instance Id: {1}",
                executionContext.ActivityInstanceId,
                executionContext.WorkflowInstanceId);

            // Create the context
            IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

            if (context == null)
            {
                throw new InvalidPluginExecutionException("Failed to retrieve workflow context.");
            }

            tracingService.Trace("Class1.Execute(), Correlation Id: {0}, Initiating User: {1}",
                context.CorrelationId,
                context.InitiatingUserId);

            IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
            IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

            try
            {
                IWorkflowContext wfContext = executionContext.GetExtension<IWorkflowContext>();
                IOrganizationServiceFactory wfServiceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
                IOrganizationService wfService = wfServiceFactory.CreateOrganizationService(context.InitiatingUserId);
                int batchId = (int)this.BatchNumber.Get(executionContext);
                Guid thisGuid = ((EntityReference)this.ThisEntity.Get(executionContext)).Id;
                RollupCalculations.FireBatch(service, tracingService, batchId, thisGuid, context.Depth);
            }
            catch (FaultException<OrganizationServiceFault> e)
            {
                tracingService.Trace("Exception: {0}", e.ToString());

                // Handle the exception.
                throw;
            }

            tracingService.Trace("Exiting Class1.Execute(), Correlation Id: {0}", context.CorrelationId);
        }
        [Input("Batch Number")]
        public InArgument<int> BatchNumber { get; set; }
        [Input("This Config Entity")]
        [ReferenceTarget("rh_rollingcalculationtrigger")]
        public InArgument<EntityReference> ThisEntity { get; set; }
    }

FireBatch: query all records with batchID = x and update them with a random number.

// In this case, the processingSequenceNumber is going to be the value from the CRM batch controller entity.
public static void FireBatch(IOrganizationService service, ITracingService trace, int processingSequenceNumber)
        {
            // First, get all Connections with that batch ID.
            String fetch = @"
            <fetch version='1.0' output-format='xml-platform' mapping='logical' distinct='false'>
              <entity name='connection'>
                <attribute name='connectionid' />
                <order attribute='description' descending='false' />
                <filter type='and'>
                  <condition attribute='rh_systemfieldcalculationbatch' operator='eq' uiname='' uitype='product' value='" + processingSequenceNumber + @"' />
                </filter>
              </entity>
            </fetch>";

            // Now do a bulk update of them. 
            EntityCollection result = service.RetrieveMultiple(new FetchExpression(fetch));
            trace.Trace("Processing " + result.Entities.Count + " records on batch " + processingSequenceNumber);

            ExecuteMultipleRequest multipleRequest = new ExecuteMultipleRequest()
            {
                Settings = new ExecuteMultipleSettings()
                {
                    ContinueOnError = false,
                    ReturnResponses = false
                },
                Requests = new OrganizationRequestCollection()
            };

            Random rand = new Random();
            
            int testLimit = 0;
            if (result != null && result.Entities.Count > 0)
            {
                // In this section _entity is the returned one
                foreach (Entity _entity in result.Entities)
                {
                    Guid thisGUID = ((Guid)_entity.Attributes["connectionid"]);
                    var newConnection = new Entity("connection");
                    newConnection.Id = thisGUID;
                    // Note here that we're just dropping a random number in the field. We don't care what the number is, since all it's doing is triggering the subsequent plugin.
                    newConnection["rh_systemfieldrecalculationtrigger"] = rand.Next(-2147483647, 2147483647);

                    UpdateRequest updateRequest = new UpdateRequest { Target = newConnection };
                    multipleRequest.Requests.Add(updateRequest);
                    //trace.Trace("Completed record #" + testLimit);
                    testLimit++;
                }
                service.Execute(multipleRequest);

            }
        }

Warning: use ExecuteMultiple to avoid timeouts.

Now, create a plugin against the Connection to do whatever it is you want. It should fire on change of the rh_systemfieldrecalculationtrigger field.

        protected void ExecutePostConnectionUpdate(LocalPluginContext localContext)
        {
            if (localContext == null)
            {
                throw new ArgumentNullException("localContext");
            }

            IPluginExecutionContext context = localContext.PluginExecutionContext;
            Entity postImageEntity = (context.PostEntityImages != null && context.PostEntityImages.Contains(this.postImageAlias)) ? context.PostEntityImages[this.postImageAlias] : null;
            ITracingService trace = localContext.TracingService;
            IOrganizationService service = localContext.OrganizationService;

            // In this method I do the logic I want to do against the specific record.
            RollupCalculations.SalesToDate(service, trace, postImageEntity);
        }

The final piece is to, well, get it rolling. First, create 1000 instances of rh_rollingcalculationtrigger, setting Batch ID’s 0-999.

Remember, we can create a recursive workflow with the 60 minute workaround. I’m setting it to 61 just to be safe.

2015-10-06 15_09_06-Document1 - Word

Manually fire the workflow once on each of your 1000 recalculation entities. Congratulations, you have perpetual recalculation.

I recommend setting up bulk deletion jobs to remove the system job records this creates. It can be a lot.

Outlook Plugin: Server-Side Sync vs CRM For Outlook

Published by:

In Microsoft Dynamics CRM 2015 Online with Exchange Online, you have the option for the Outlook client to work over “Server-Side Synchronization” or “CRM for Outlook.”

Which one should you choose?

Server-side sync uses server-to-server communication and the Outlook client basically runs everything through the user’s desktop computer. Things to keep in mind:

  1. If you use the Outlook version, and a tracked event occurs such as sending an email from the web client, it won’t be processed unless, or until, Outlook is open and connected on the user’s desktop.
  2. If you user Server-Side Sync, tracking activities in Outlook can take some time. You can adjust the sync interval in Settings -> Administration -> System Settings -> Outlook to as little as one minute. This may not be a problem, but if you are planning to use the “Convert To” or “Add Connection” functions in any meaningful fashion, you’re going to have to wait for the sync to occur before you can use those buttons. They even take away the Synchronize button! Your pilot users will undoubtedly yell “why is Convert To greyed out?!?” or “why is Add Connection greyed out??”
  3. Server-Side Sync, with CRM Online 2015 Update 1, can synchronize with Exchange based on e-mail folders.

Microsoft also says Server-Side Sync can improve Outlook performance, which makes sense, but I haven’t seen such a huge difference.

Fret Not: You can have the best of both worlds.

I’ve found that if I set outbound e-mail to process by Server-Side Sync, and inbound emails and other activities via the Outlook client, when you track items in Outlook they go up to the server instantaneously, enabling the Convert To and Add Connection buttons. But you can also send email from the mobile or web apps even if your laptop is closed in your bag.

You can set this globally in Settings -> Email Configuration -> Email Configuration Settings.

2015-08-16 14_05_11-Email Configuration - Microsoft Dynamics CRM

Outlook Plugin with CRM Online & Office 365: Tracked Items Aren’t Tracked

Published by:

We recently ran across an issue when setting up CRM Online 2015 Update 1 with Office 365 Exchange Online. A user installed the Outlook plugin, and could click the Track and Set Regarding functions – even select which record it should be Regarding – but the record never made it to CRM. The original symptom wasthe “Convert To” function was grayed out no matter what we did.

The issue is that in some cases, the Email Server Profile defaults to a value that doesn’t work. Both Incoming Server Location and Outgoing Server Location need to be set to

https://outlook.office365.com/EWS/Exchange.asmx

But how? The field disappears almost immediately, and you can’t create a new form against this entity, nor can you create workflows.

So I forced it to reopen by using the F12 debugger and setting the field to be editable. For example, incomingserverlocation can be edited by opening the F12 debugger and removing the applicable clause. For example, here, you would remove the part that says disabled=”disabled” altogether. This opens up the field to be edited.

2015-08-05 14_02_04-Inspector - https___xxxxx.crm.dynamics.com_main.aspx_etc=9605&extraq

You need to do this for the three fields.

  • Autodiscover Server Location = No
  • Incoming Server Location = https://outlook.office365.com/EWS/Exchange.asmx
  • Outgoing Server Location = https://outlook.office365.com/EWS/Exchange.asmx

2015-08-05 13_54_43-Email Server Profile_ Test 2 - Microsoft Dynamics CRM

Then associate your user(s) to the profile. You then need to approve them and click the Test button.

But mine came back with this error:

Email cannot be sent because the email address of the mailbox John Smith requires an approval by an Office 365 administrator. The mailbox has been disabled for sending email and the owner of the email server profile Test 2 has been notified.

Make sure the person doing the approving and testing is both an Office 365 admin and a CRM admin.

CRM for Phones: “A primary entity column cannot have a dot in its name”

Published by:

When using the new CRM For Phones and CRM for Tablets apps against Microsoft Dynamics CRM 2015 Online Update 1, I was getting the following error.

“Sorry, something went wrong while initializing the app. Please try again, or restart the app.”

I looked into some of the standard troubleshooting steps for this one and none of them applied.

So I enabled tracing. It was a big PITA to install all 150mb of iTunes and wait for sync, but it was worth it because I found the following error in the logs.

Error Message:Microsoft.Crm.CrmInvalidOperationException: Unable to process the following entities - contact. Please try disabling the entities and try again. ---> Microsoft.Crm.CrmException: A primary entity column cannot have a dot in its name

The key being A primary entity column cannot have a dot in its name.

Because “having a dot in its name” alludes to an aliased field, I checked that my custom code didn’t contain anything odd – it didn’t. I finally found the problem in a corrupt View definition.

2015-07-27 13_36_02-View_ Contacts_ No Orders in Last 6 Months - Microsoft Dynamics CRM - Internet E

2015-07-27 14_44_14-Message from webpage

After removing the bad columns, the mobile app fired up no problem.