domingo, 16 de marzo de 2014

Consultando la auditoría de Dynamics CRM

Hoy me propongo la consulta del registro de Auditoría. La auditoría en Dynamics CRM 2013, se almacena en una especie de entidad, pero con limitaciones, y que no puede ser accesible por las búsquedas avanzadas estándar.
Solamente pueden consultarse en los propios registros o de forma general en Configuración->Auditoría->Vista resumen de auditoría:
image
image
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:

image

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.

1 comentario:

  1. Demian, saludos

    Es viable generar un reporte con Reporting Srevices, que consulte los registros de auditoria de una entidad en especifico?, de ser así, que tablas debería consultar en SQL ademas de AuditBase o la vista Audit.}

    Gracias

    ResponderEliminar