The Mysterious Duplicate Status Records in CRM Audits

30 March 2017
Daniel Cai

At KingswaySoft, we always try to embrace new platform features as much and as soon as we can. However, in our support effort in helping one of our clients, we ran into a very strange situation which is related to the use of the enhanced Update method for Dynamics 365/CRM that we have started to take advantage of since our v6.0 release of the SSIS Integration Toolkit software, and we believe it offers a better performance than using the legacy SetState service calls to update CRM record's status.

The symptom of the problem is, when we try to update a CRM record's statecode and statuscode fields using a mapping like something below, it will create duplicate audit records for those two fields.

StateCode Mappings in CRM Destination Component

As I just mentioned, our software tries to utilize the new API capabilities whenever possible, so with the above CRM destination configuration (the component is using the Update action), we are making one Update service call for each record that is passed to the destination component. The Update service is essentially the following CRM SDK code if translated.

using (var service = new OrganizationService(crmConnection))
    Entity account = new Entity("account");
    account["accountid"] = new Guid("0C2D5AC7-B7E4-E411-80E9-C4346BAC7DA8");
    account["statecode"] = new OptionSetValue(1); //inactive
    account["statuscode"] = new OptionSetValue(2); //inactive
    var request = new UpdateRequest() { Target = account };
    var response = (UpdateResponse)service.Execute(request);

The problem is, when we run the above code (or the destination component as shown above), it will always end up creating two duplicate status change records in CRM Audit log for one Update service call.

Duplicate StateCode audit records when using the New CRM Update API Capability

You can see, there appears to be two CRM Update service calls, while there was actually only one sent from client side. I have done some research and cannot make sense of this situation. My conclusion is, this is a platform bug which was introduced when the feature was developed.

This may not be a big issue for many people, but it is an annoying problem if you care about the truthfulness of your CRM audit records. The solution is rather simple, you just have to use the old-fashioned code by making the old-school setstate service call, such as the following:

using (var service = new OrganizationService(crmConnection))
    var setStateRequest = new SetStateRequest()
        EntityMoniker = new EntityReference
            LogicalName = "account",
            Id = new Guid("0C2D5AC7-B7E4-E411-80E9-C4346BAC7DA8")
        State = new OptionSetValue(1), //inactive
        Status = new OptionSetValue(2) //inactive
    var setStateResponse = (SetStateResponse)service.Execute(setStateRequest);

In our case, we introduced a new connection property called ApiVersion in our v7.4 release. You can use a number lower than v7.1 (corresponding to CRM 2015 Update 1) such as "7.0" as your CRM connection manager's ApiVersion option so that we skip the optimization and use the old setstate call. To avoid confusions, we didn't surface this option in our connection manager UI. It is available if you use SSIS Properties window.

ApiVersion option for CRM Connection Manager

Once you have changed your code (or made corresponding change to the ApiVersion as shown above in our CRM connection manager), you will see the right audit data in CRM system when you try to run the code or SSIS package in our case.

Clean StateCode audit records when using the Old CRM SetState service call

You can see that we are now getting only one audit record for each service call, not more duplicates, so less confusions when reading the audit logs. Do note the audit event is Activate/Deactivate when using SetState request vs Update when using the new API capability.

Going the old route is not always the preferred choice, and it will have a bit performance impact, as we will have to make two service calls instead of one if we have more than just those two fields for update.

I hope this is helpful if you ever run into this problem, whether you use SDK to write your program, or use our software for your integration purpose.