D365 Überwachungsverlauf abfragen oder löschen mit C# und FetchXml

Der Überwachungsverlauf ist ein sehr gutes Feature von modellgesteuerten Apps, um die Änderungen an Feldern einer Tabelle (Entität) nachverfolgen zu können. Das Abfragen des Überwachungsverlaufs ist jedoch etwas schwierig. Leider unterstützen gängige Abfragemechanismen wie Power Automate CDS-Konnektoren, LinQ oder einfach FetchXml dies noch nicht. In diesem Beitrag wird eine Optionen besprochen, wie der Überwachungsverlauf mit C# Code abgefragt werden kann.

Überwachungsverlauf

Wenn die Überwachung aktiviert ist, sind Prüfungsdetails für die folgenden vier Rubriken verfügbar:

  • Änderungsdatum
  • Geändertes Feld
  • Alter Wert
  • Neuer Wert

Mittels SDK Nachrichten, RetrieveRecordChangeHistoryRequest und RetrieveRecordChangeHistoryResponse kann der Überwachungsverlauf in C# abgefragt werden.

Wie funktioniert das

Wir benötigen die IDs (GUID) von Entitäten, mit denen werden wir den Überwachungsverlauf abfragen. Ich verwende eine FetchXml-Abfrage, um IDs abzurufen, aber es kann je nach Implementierung und Anforderung ein anderer Mechanismus verwendet werden.

string fetch = @"<fetch>
                  <entity name='contact'>
                    <filter>
                      <condition attribute='firstname' operator='begins-with' value='anonymisiert' />
                      <condition attribute='lastname' operator='begins-with' value='anonymisiert' />
                      <filter>
                        <condition attribute='modifiedon' operator='on-or-after' value='2022-03-01' />
                      </filter>
                    </filter>
                  </entity>
                </fetch>";

FetchXml ist auf maximal 5000 Entitäten, die zurückgegeben werden können, limitiert. Mittels Paging Mechanismus können auch mehr als 5000 Datensätze bearbeitet werden:

public static List<Entity> GetRecords(string fetch, IOrganizationService crmService)
{
    var moreRecords = false;
    int page = 1;
    var cookie = string.Empty;
    List<Entity> Entities = new List<Entity>();
    do
    {
        var xml = string.Format(fetch, cookie);
        var collection = crmService.RetrieveMultiple(new FetchExpression(xml));

        if (collection.Entities.Count >= 0)
        {
            Entities.AddRange(collection.Entities);
        }

        moreRecords = collection.MoreRecords;
        if (moreRecords)
        {
            page++;
            cookie = string.Format("paging-cookie='{0}' page='{1}'", System.Security.SecurityElement.Escape(collection.PagingCookie), page);
        }
    } while (moreRecords);

    return Entities;
}

Die abgeholten Entitäten können dann verwendet werden, um beispielsweise den Überwachungsverlauf der Entität zu löschen:

public static DeleteRecordChangeHistoryResponse DeleteRecordChangeHistory(Entity entity, IOrganizationService service)
{
    DeleteRecordChangeHistoryRequest deleteRecordChangeHistoryRequest = new DeleteRecordChangeHistoryRequest()
    {
        Target = entity.ToEntityReference()
    };

    return (DeleteRecordChangeHistoryResponse)service.Execute(deleteRecordChangeHistoryRequest);
}

oder einfach abzufragen:

public static GetRecordChangeHistoryResponse GetAuditHistory(Entity item, IOrganizationService crmService)
{
    var changeRequest = new RetrieveRecordChangeHistoryRequest { Target = new EntityReference(item.LogicalName, item.Id) };

    return (RetrieveRecordChangeHistoryResponse)crmService.Execute(changeRequest);
}

Bei der Arbeit mit Massendaten muss aber immer das 2 Minuten Timeout des CRM beachtet werden.

Wenn größere Mengen an Daten verarbeitet werden, ist auch eine alternative Technologie, wie beispielsweise Azure Functions, zu überlegen.