In a requirement we were asked to show one specific field change history in a custom portal written in PHP, our immediate action was to call RetrieveAttributeChangeHistory via WebApi but if was just returning the list of value history without any other fields but getting CahngedOn and ChangedBy field was our requirement. (see details in github issue here).

We finally headed to our good old CRM SDK libraries and written code in C# then access this via Dynamics 365 WebApi using Dynamics 365 Custom Action.

I’m sharing here core logic, it can be used either in Action or any other place where CRM SDK C# libraries can be consumed.

Model class to get history as a list

public class FieldHistory
    public string ChangedBy { get; set; }
    public DateTime ChangedOn { get; set; }
    public string Oldvalue { get; set; }
    public string NewValue { get; set; }

Function to get and parse field history

Below code is using FormattedValues which retrieve user readable component out of OptionSet and EntityReference fields, this can be modified according to your requirements.

public static List<FieldHistory> GetFieldHistory(string entityLogicalName, Guid recordId, string fieldName, IOrganizationService service)
    // Retrieving change history fro given attribute/field
    var attributeChangeHistoryRequest = new RetrieveAttributeChangeHistoryRequest
        Target = new EntityReference(entityLogicalName, recordId),
        AttributeLogicalName = fieldName

    var attributeChangeHistoryResponse = (RetrieveAttributeChangeHistoryResponse)service.Execute(attributeChangeHistoryRequest);
    var details = attributeChangeHistoryResponse.AuditDetailCollection;

    // List to store changes
    var fieldHistory = new List<FieldHistory>();

    foreach (var detail in details.AuditDetails)
        var detailType = detail.GetType();
        if (detailType == typeof(AttributeAuditDetail))
            // retrieve old & new value of each action of each audit change from AttributeAuditDetail
            var attributeDetail = (AttributeAuditDetail)detail;

            var userName = attributeDetail.AuditRecord.GetAttributeValue<EntityReference>("userid").Name;
            var changedOn = attributeDetail.AuditRecord.GetAttributeValue<DateTime>("createdon");
            var newValue = attributeDetail.NewValue.FormattedValues[fieldName];
            var oldValue = attributeDetail.OldValue?.FormattedValues[fieldName];

            fieldHistory.Add(new FieldHistory
                ChangedBy = userName,
                ChangedOn = changedOn,
                Oldvalue = oldValue,
                NewValue = newValue

            return fieldHistory;

If you observe I have use safe navigation operator for var oldValue = attributeDetail.OldValue?.FormattedValues[fieldName]; this is because for the first occurrence OldValue will always remain null.

This code is good if we are using in C# specific requirement, while it’s a workaround if we need field history via WebAPI. Let’s keep an eye in official documentation and in this issue if Microsoft returns more meaningful response in future.

I hope this helps.

Buy Me A Coffee
©Ashish Vishwakarma. | Read More Posts