Skip to content

Updating data with FluentCRM

Roger Hill edited this page Dec 4, 2020 · 4 revisions

Updating with FluentCRM

One of the initial drivers of the FluentRM project was a problem I was having with updates.
I had a web service that was being passed a DTO (Data Transfer Object). This DTO had nothing on it to say whether a particular field had changed or not, none of the fields were nullable, and the existing code I inherited simply took the DTO and set all of the corresponding fields in the CRM entity before calling update.

Apart from the time this took to execute all of these updates, the audit log was full of entries relating to the update saying things like Attribute "name" had been changed from "Smith" to "Smith".
In some cases these "updates" which were not "changes" were triggering plugins or workflows, leading to unexpected results in the system.
Also, because the DTO had no concept of null values, in many cases CRM fields were being set to a value when they should not have been (e.g. an attibute being set to an empty string when it should have been null).

I started trying to address this by reading the attribute values before updating, then comparing them to the DTO then adding these to an update entity only when absolutely required. Of course, I soon realised that I was writing the same code (and creating the same bugs) over and over again, so I started looking at writing a CRM interface library to automate this process and take the pain out of it. I felt that a "fluent" stlye of interface was appropriate, the use of generics could also be leveraged to simplify it, and I also wanted to create something that could be extended to work with different, possibly custom entity types found in CRM.

The final result of this is the FluentCRM nuget package, which can be used to craft a more fluent interface to CRM from within your applications.

Updating data with FluentCRM

There are a number of steps in any FluentCRM call that need to be considered. These are very loosely: -

  1. Decide which entity you want to work with. (e.g. Contact)
  2. Select the set of records of that entity type to operate on. (Contacts where the last name is "Jones")
  3. Decide what operations you want to carry out on the entity (E.g. Update the name and phone number from DTO fields)
  4. Once everything has been assembled, call Execute() to set the ball rolling and start making calls out to CRM.

Selecting the Entity type to operate on.

Choosing the entity to operate on is simple as choosing the correct class to start with. There are a number of predefined classes for commonly used CRM entities (for Example FluentAccount, FluentContact, FluentCase) If you need to operate on an entity not included in the base package, or a custom entity of your own this can quite easily be accommodated - see Extending FluentCRM

Selecting a set of Entities of the chosen type

FluentCRM takes a set-oriented approach to its operations - it doesn't really care whether your set contains zero, one or many entities. As long as they are all of the same type, it will work just fine.

The entities to be operated on can be specified two basic ways: -

  • By Id value
  • Based on attribute values

For Id values it is simple as saying "Contact(idGuidValue)" and FluentCRM will take care of everything else for you. If the given Id value does not exist, none of the selected operations will take place (no closures will be called).
No exception will be thrown in this case.

For choosing a set of records based on attribute values, it can be as simple as saying '.Where("accountnumber").Equals("123456")'

Note the set of Where-criteria is by no means comprehensive - I've implememented those that were useful to me at the time.

Operate on the selected records

FluentCRM supports various operations on the selected records. One is to make use of attributes extracted from the entity, but the one we will talk about here is to update an attribute in the entity.

The original problem that FluentCRM started trying to solve was "false updates" to entities, so I decided to call the update function "WeakUpdate" - on the same lines as .Net framework functions such as WeakReference, it is a "Weak" update in the sense that it will only carry out an update to the CRM server where the value has changed and is not null. This does mean that a weak update will cause a read from CRM so that it has values to compare against before trying to do the update.
Note however that the WeakUpdate() function will only read attributes that it absolutely needs to - it won't go off and start reading all the attributes. (Although you can make it do this, if you really want to!)

You can specify the update value as a static value that will be bound at the time of the WeakUpdate() call.
In many cases is is more useful to use the closure form where the value is determined dynamically during the update process itself.
This is a quite subtle difference, that does require a degree of understanding (of closures) and consideration.
Failure to do so will lead to FluentCRM not doing what you expect!

Static Update

FluentContact.Contact(contact1.Id)
    .WeakUpdate("lastname", dto.name )
    .Execute();

In this case every selected entity will get the same value (although only one entity will be selected in this case)

Dynamic update

var smithSequence = 0;
FluentContact.Contact()
    .Where("lastname").Equals("Smith")
    .WeakUpdate("new_smithsequence", (int i) => smithSequence++ )
    .Execute();

In this case every contact with a last name of "Smith" will be given a unique sequence number.

What would happen if we didn't use a closure here?

var smithSequence = 0;
FluentContact.Contact()
    .Where("lastname").Equals("Smith")
    .WeakUpdate("new_smithsequence", smithSequence++ )
    .Execute();

In this case every contact with a last name of "Smith" would be assigned the same sequence number of "0".
This is probably not what we intended.

How do we specify the CRM connection?

You may have noticed that in none of these calls has the value of IOrganizationService been explicitly stated. So Where are we getting this from?

In this case the connection to the CRM system has been set globally by setting the value of FluentCRM.FluentCRM.StaticService to the value of the conection IOrganizationService.
Once this has been done, all FluentCRM calls will use this CRM connection by default.

However, it is still possible to explicitly give the CRM connection IOrganizationService in each call to FluentCRM. Where it is given it will override the value given in StaticService.