Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :
Microsoft Dynamics CRM (Archived)

Retrieving audit data via console app

(0) ShareShare
ReportReport
Posted on by 8

I have written a console app to try and retrieve audit data from a filtered list of Cases. The audit data I want is specific, it's from the field Status Reason - I want the Change Date value of when it was last updated to the value Pending Closure

66057.Capture1.PNG

The result from my console app is an error: 

546378.Capture.PNG

 

This is the code I've got so far, but i just can't quite get it to show any audit data at all :(

string EntitySchemaName = "incident", AttributeSchemaName = "statuscode";

            foreach (var caseRecords in results.Entities)
            {
                string CaseName = caseRecords.Attributes["title"].ToString();
                string CaseNumber = caseRecords.Attributes["ticketnumber"].ToString();
                string CaseGUID = caseRecords.Attributes["incidentid"].ToString();
                _CaseId = new Guid(CaseGUID);

                try
                {
                    RetrieveAttributeChangeHistoryRequest attributeChangeHistoryRequest = new RetrieveAttributeChangeHistoryRequest
                    {
                        Target = new EntityReference(EntitySchemaName, _CaseId),
                        AttributeLogicalName = AttributeSchemaName
                    };

                    RetrieveAttributeChangeHistoryResponse attributeChangeHistoryResponse = (RetrieveAttributeChangeHistoryResponse)service.Execute(attributeChangeHistoryRequest);
                    
                    foreach (var EachEditRecord in attributeChangeHistoryResponse.AuditDetailCollection.AuditDetails)
                     {
                        AttributeAuditDetail attributeDetail = (AttributeAuditDetail)EachEditRecord;

                        System.Console.WriteLine(attributeDetail);
                        System.Console.WriteLine(EachEditRecord);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine("Failed to fetch data. \nReason {0}", e.Message);
                }
                Console.ReadLine();

*This post is locked for comments

  • Suggested answer
    Nishant Rana Profile Picture
    11,323 Microsoft Employee on at
  • Suggested answer
    Community Member Profile Picture
    on at
    RE: Retrieving audit data via console app

    This is how I pull the data - my Code is starting at the loop where you are getting the AuditDetails, you can remove the if statement that is looking for statuscode if you are looking at all/different attributes.

                    foreach (var myDetail in auditDetailCollection.AuditDetails) {
    
                        var detailType = myDetail.GetType();
                      
                        if (detailType == typeof(AttributeAuditDetail))
                        {
                            var attributeDetail = (AttributeAuditDetail)myDetail;
    
                            foreach (KeyValuePair<String, object> attribute in attributeDetail.NewValue.Attributes)
                            {
                                // get the optionset if it is a statuscode value
                                if (attribute.Key == "statuscode") {
                                    OptionSetValue myVal = (OptionSetValue)attribute.Value;
                                    int myStatusCodeId = myVal.Value;
    
                                    // Do stuff with your New Value
                                }
                            }
    
                            foreach (KeyValuePair<String, object> attribute in attributeDetail.OldValue.Attributes)
                            {
                                // get the optionset if it is a statuscode value
                                if (attribute.Key == "statuscode")
                                {
                                    OptionSetValue myVal = (OptionSetValue)attribute.Value;
                                    int myStatusCodeId = myVal.Value;
    
                                    // Do Stuff with your Old Value
                                }
                            }
                        }
                    }


  • Suggested answer
    Seren S Profile Picture
    402 on at
    RE: Retrieving audit data via console app

    Hi G. Vetere,

    I think this is not related to CRM, only a logical error in indexers.

    If you should update your class with defining the dimension of the array;

        public class Case_List
        {
            private string[] address = new string[1];
            public string this[int index]
            {
                get
                {
                    return address[index];
                }
    
                set
                {
                    address[index] = value;
                }
            }
        }


    Then, you can get&set your default indexer.

    Case_List date = new Case_List();
    date[0] = auditDetailsResponse.AuditDetail.AuditRecord.Attributes["createdon"].ToString();
    Console.WriteLine(string.Format("Audit CreatedOn Value in Case_List{0} ", date[0].ToString()));

    Cheers, Seren.

  • G.Vetere Profile Picture
    8 on at
    RE: Retrieving audit data via console app

    Hi Seren Serifoglu 

    I'm trying to now restrict the audit data to only the most recent one

    I just want to know the most recent createdon date for statuscode changed

    I've tried using an indexer and just showing the first one but it gives me an error:

     2046.app.PNG

    Here's my code atm:

    public class Case_List
            {
                private string[] address = new string[0];
                public string this[int index]
                    {
                        get
                        {
                            return address[index];
                        }
    
                        set
                        {
                            address[index] = value;
                        }
                    }
            }
    .
    .
    .
    .
    .
    .
    var auditDetailsResponse = (RetrieveAuditDetailsResponse)service.Execute(auditDetailsRequest);
    
                                    if (auditDetailsResponse != null && auditDetailsResponse.AuditDetail != null && auditDetailsResponse.AuditDetail.AuditRecord != null)
                                    {
                                        Entity auditEntity = auditDetailsResponse.AuditDetail.AuditRecord;
    
                                        if (auditEntity.Attributes.Contains("createdon"))
                                        {
                                            Case_List date = new Case_List();
                                            date[0] = auditDetailsResponse.AuditDetail.AuditRecord.Attributes["createdon"].ToString();
    
                                            //var dateValue = auditDetailsResponse.AuditDetail.AuditRecord.Attributes["createdon"].ToString();
                                            Console.WriteLine(string.Format("Updated on {0}", date[0]));
                                                
                                        }
                                    }


    Thanks!

  • Verified answer
    Seren S Profile Picture
    402 on at
    RE: Retrieving audit data via console app

    Hi G.Vetere,

    To achieve this, retrieve function is not enough to get all the attributes of the audit record. You should use "RetrieveAuditDetailsRequest" for further details.

                foreach (var EachEditRecord in attributeChangeHistoryResponse.AuditDetailCollection.AuditDetails)
                {
                    var detailType = EachEditRecord.GetType();
                    if (detailType == typeof(Microsoft.Crm.Sdk.Messages.AttributeAuditDetail))
                    {
                        Microsoft.Crm.Sdk.Messages.AttributeAuditDetail attributeDetail = (Microsoft.Crm.Sdk.Messages.AttributeAuditDetail)EachEditRecord;
                        foreach (KeyValuePair<String, object> attribute in attributeDetail.NewValue.Attributes)
                        {
                            if (attributeDetail.OldValue.Contains(attribute.Key))
                                oldValue = ((OptionSetValue)attributeDetail.OldValue[attribute.Key]).Value.ToString();
                            if (attributeDetail.NewValue.Contains(attribute.Key))
                                newValue = ((OptionSetValue)attributeDetail.NewValue[attribute.Key]).Value.ToString();
    
                            Console.WriteLine("Attribute: {0}, old value: {1}, new value: {2}", attribute.Key, oldValue, newValue);
    
                            //****************
                            //Details of Audit
                            var auditDetailsRequest = new Microsoft.Crm.Sdk.Messages.RetrieveAuditDetailsRequest
                            {
                                AuditId = attributeDetail.AuditRecord.Id
                            };
                            var auditDetailsResponse = (Microsoft.Crm.Sdk.Messages.RetrieveAuditDetailsResponse)_service._orgService.Execute(auditDetailsRequest);
                            if (auditDetailsResponse != null && auditDetailsResponse.AuditDetail != null && auditDetailsResponse.AuditDetail.AuditRecord != null)
                            {
                                Microsoft.Xrm.Sdk.Entity auditEntity = auditDetailsResponse.AuditDetail.AuditRecord;
                                if (auditEntity.Attributes.Contains("createdon"))
                                {
                                    Console.WriteLine(string.Format("Audit CreatedOn {0}", auditDetailsResponse.AuditDetail.AuditRecord.Attributes["createdon"]));
                                }
                            }
                        }
    
                        System.Console.WriteLine(attributeDetail);
                        System.Console.WriteLine(EachEditRecord);
                    }
                }


    I dropped useful links for you to extend your works.

    https://docs.microsoft.com/en-us/previous-versions/dynamicscrm-2016/developers-guide/gg309735(v=crm.8)

    https://docs.microsoft.com/en-us/previous-versions/dynamicscrm-2016/developers-guide/gg308134(v%3Dcrm.8)

    Cheers, Seren.

  • G.Vetere Profile Picture
    8 on at
    RE: Retrieving audit data via console app

    Thank you Seren!

    One more thing, I can see you can pull newValue and oldValue, but i can't find the variable for the Change Date field?

  • Verified answer
    Seren S Profile Picture
    402 on at
    RE: Retrieving audit data via console app

    Hi G.Vetere,

    You have to cascade your returned value into a compatible type of XRM classes then you are able to get the exact value.

    e.g.

    oldValue = ((OptionSetValue)attributeDetail.OldValue[attribute.Key]).Value.ToString();


    It is also the same for the EntityReferences (lookup fields) by using (EntitiyReference) as a cascade member in the first.

    Hope it helps.

    Cheers, Seren.

  • G.Vetere Profile Picture
    8 on at
    RE: Retrieving audit data via console app

    Hi Seren,

    Thanks for helping, it helped me get much further and i think im also there, but now I've encountered a new issue:

    I get a message displayed saying: Microsoft.Xrm.Sdk.OptionSetValue  where the value should be, i'm not sure what i'm missing other than converting it to a string?

    2604.Capture3.PNG

    string EntitySchemaName = "incident", AttributeSchemaName = "statuscode";

                foreach (var caseRecords in results.Entities)
                {
                    string CaseName = caseRecords.Attributes["title"].ToString();
                    string CaseNumber = caseRecords.Attributes["ticketnumber"].ToString();
                    string CaseGUID = caseRecords.Attributes["incidentid"].ToString();
                    String oldValue = "(no value)", newValue = "(no value)";
                    _CaseId = new Guid(CaseGUID);
                                                    
                    try
                    {
                        RetrieveAttributeChangeHistoryRequest attributeChangeHistoryRequest = new RetrieveAttributeChangeHistoryRequest
                        {
                            Target = new EntityReference(EntitySchemaName, _CaseId),
                            AttributeLogicalName = AttributeSchemaName
                        };
    
                        RetrieveAttributeChangeHistoryResponse attributeChangeHistoryResponse = (RetrieveAttributeChangeHistoryResponse)service.Execute(attributeChangeHistoryRequest);
    
                        foreach (var EachEditRecord in attributeChangeHistoryResponse.AuditDetailCollection.AuditDetails)
                        {
                        var detailType = EachEditRecord.GetType();
                            if (detailType == typeof(AttributeAuditDetail))
                            {
                                AttributeAuditDetail attributeDetail = (AttributeAuditDetail)EachEditRecord;
    
                                foreach (KeyValuePair<String, object> attribute in attributeDetail.NewValue.Attributes)
                                {
                                    if (attributeDetail.OldValue.Contains(attribute.Key))
                                        oldValue = attributeDetail.OldValue[attribute.Key].ToString();
                                        newValue = attributeDetail.NewValue[attribute.Key].ToString();
    
                                        Console.WriteLine("Attribute: {0}, old value: {1}, new value: {2}", attribute.Key, oldValue, newValue);
                                }
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Failed to fetch data. \nReason {0}", e.Message);
                    }
                    Console.ReadLine();
    
                    System.Console.WriteLine(caseRecords.Attributes["title"]);
                    System.Console.WriteLine(caseRecords.Attributes["ticketnumber"]);


  • Suggested answer
    Seren S Profile Picture
    402 on at
    RE: Retrieving audit data via console app

    Hi G.Vetere,

    You need to check the type of Audits. Because it contains the Microsoft.Crm.Sdk.Messages.AttributeAuditDetail which is the one you want to use, and it can also contain the Microsoft.Crm.Sdk.Messages.AuditDetail which is used for messages of Audits itself. (e.g: Audit Enabled: System creates this when you first enable the audits)

    So if you update your code with adding validation into your foreach loop, then you are all good to go.

               string EntitySchemaName = "incident", AttributeSchemaName = "statuscode";
    
                foreach (var caseRecords in results.Entities)
                {
                    string CaseName = caseRecords.Attributes["title"].ToString();
                    string CaseNumber = caseRecords.Attributes["ticketnumber"].ToString();
                    string CaseGUID = caseRecords.Attributes["incidentid"].ToString();
                    _CaseId = new Guid(CaseGUID);
    
                    try
                    {
                        RetrieveAttributeChangeHistoryRequest attributeChangeHistoryRequest = new RetrieveAttributeChangeHistoryRequest
                        {
                            Target = new EntityReference(EntitySchemaName, _CaseId),
                            AttributeLogicalName = AttributeSchemaName
                        };
    
                        RetrieveAttributeChangeHistoryResponse attributeChangeHistoryResponse = (RetrieveAttributeChangeHistoryResponse)service.Execute(attributeChangeHistoryRequest);
    
                        foreach (var EachEditRecord in attributeChangeHistoryResponse.AuditDetailCollection.AuditDetails)
                        {
                            var detailType = EachEditRecord.GetType();
                            if (detailType == typeof(Microsoft.Crm.Sdk.Messages.AttributeAuditDetail))
                            {
                                AttributeAuditDetail attributeDetail = (AttributeAuditDetail)EachEditRecord;
    
                                System.Console.WriteLine(attributeDetail);
                                System.Console.WriteLine(EachEditRecord);
                            }
                        }
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Failed to fetch data. \nReason {0}", e.Message);
                    }
                    Console.ReadLine();
                }

    Hope it helps.

    Cheers, Seren.

  • Suggested answer
    Flydancer Profile Picture
    1,332 on at
    RE: Retrieving audit data via console app

    Like the error message stated already: your cast is invalid.

    Try using the relevant code snippet from https://docs.microsoft.com/en-us/previous-versions/dynamicscrm-2016/developers-guide/hh547448%28v%3dcrm.8%29 or andrew's link

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

🌸 Community Spring Festival 2025 Challenge Winners! 🌸

Congratulations to all our community participants!

Adis Hodzic – Community Spotlight

We are honored to recognize Adis Hodzic as our May 2025 Community…

Kudos to the April Top 10 Community Stars!

Thanks for all your good work in the Community!

Leaderboard > Microsoft Dynamics CRM (Archived)

#1
Mohamed Amine Mahmoudi Profile Picture

Mohamed Amine Mahmoudi 83 Super User 2025 Season 1

#2
Community Member Profile Picture

Community Member 52

#3
Victor Onyebuchi Profile Picture

Victor Onyebuchi 6

Overall leaderboard

Featured topics

Product updates

Dynamics 365 release plans