Solamente pueden consultarse en los propios registros o de forma general en Configuración->Auditoría->Vista resumen de auditoría:
Si necesitamos consultas algo mas avanzadas, sólo nos queda ir por los oscuros caminos de lo no soportado (atacando directamente al SQL Server), que no lo recomiendo, o sino utilizar la SDK.
En este caso, vamos a ver un ejemplo donde recojo todos los registros de auditorías de los últimos 30 días, y en caso de ser un Update o Create, además muestro el detalle de cambios actualizados.
El ejemplo de código es el siguiente:
static void GetAudit() { //1) Consulta con Filtro de 30 días ConditionExpression cond = new ConditionExpression("createdon", ConditionOperator.LastXDays, 30); FilterExpression filter1 = new FilterExpression(); filter1.AddCondition(cond); RetrieveMultipleRequest req = new RetrieveMultipleRequest(); QueryExpression query = new QueryExpression("audit"); query.ColumnSet = new ColumnSet(true); query.Criteria.AddFilter(filter1); req.Query = query; RetrieveMultipleResponse res =(RetrieveMultipleResponse)service.Execute(req); //2) Recorremos la Auditoria devuelta foreach (Entity ent in res.EntityCollection.Entities) { //3) Leemos los datos del registro Audit record = (Audit)ent; string fecha = String.Format("Fecha:{0}", record.CreatedOn.Value.ToString()); string usuario = String.Format("Usuario:{0}", record.UserId.Name); string accion = String.Format("Action:{0}", record.FormattedValues["action"]); string operacion = String.Format("Operación:{0}", record.FormattedValues["operation"]); string registro = String.Format("Tipo:{0} Name:{1} Guid:{2}", record.ObjectId.LogicalName, record.ObjectId.Name, record.ObjectId.Id.ToString()); //4) Escribimos un log de los datos Console.WriteLine(fecha); Console.WriteLine(usuario); Console.WriteLine(accion); Console.WriteLine(operacion); Console.WriteLine(registro); //5) Si es Create o Update if (record.Action.Value == 1 || record.Action.Value == 2) { //6) Consultamos el detalle RetrieveRecordChangeHistoryRequest changeRequest = new RetrieveRecordChangeHistoryRequest(); changeRequest.Target = new EntityReference(record.ObjectId.LogicalName, record.ObjectId.Id); RetrieveRecordChangeHistoryResponse changeResponse = (RetrieveRecordChangeHistoryResponse)service.Execute(changeRequest); AuditDetailCollection details = changeResponse.AuditDetailCollection; foreach (var detail in details.AuditDetails) { //7) Por cada detalle devuelto, pintamos valor anterior y actual Audit detail_record = (Audit)detail.AuditRecord; var detailType = detail.GetType(); if (detailType == typeof(AttributeAuditDetail)) { var attributeDetail = (AttributeAuditDetail)detail; foreach (KeyValuePair<String, object> attribute in attributeDetail.NewValue.Attributes) { String oldValue = "(no value)", newValue = "(no value)"; 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); } foreach (KeyValuePair<String, object> attribute in attributeDetail.OldValue.Attributes) { if (!attributeDetail.NewValue.Contains(attribute.Key)) { String newValue = "(no value)"; String oldValue = attributeDetail.OldValue[attribute.Key].ToString(); Console.WriteLine("Attribute: {0}, old value: {1}, new value: {2}", attribute.Key, oldValue, newValue); } } } } } Console.WriteLine("---------------"); } Console.ReadLine(); }
En resumen, utilizo el mensaje ExecuteMultiple para la consulta general y luego para el detalle utilizo el mensaje RetrieveRecordChangeHistory.
El resultado obtenido en mi caso es el siguiente:
Como puede verse, tengo toda mi auditoría en un resultado ordenado, por fecha, usuario entidad y tipo de acción.
Este ejemplo es 100% funcional, tengo idea de hacer algún tipo de herramienta a futuro, que ayudarnos con los datos que hay en los registros de auditoría, para que sea mas sencillo su explotación.
Espero les sea útil este ejemplo, a mí me ha servido.