Estoy tratando de escribir un seguimiento de auditoría para Nhibernate que engancha en el evento PreUpdate. Tengo una clase AuditLogEntry (cuándo, quién, etc.) que contiene una lista de AuditLogEntryDetails (es decir, propiedades individuales que han cambiado). Si aislo la clase AuditLogEntry de la entidad que se está auditando, mi código se ejecuta sin errores. Sin embargo, si añado una lista de AuditLogEntry de la entidad auditada entonces mi código lanza una colecciónNHibernate: Actualizando colecciones durante EventListener "PreUpdateEvent"
[DomainObjects.AuditTracking.AuditLogEntry.Details] no fue procesado por ras()
error de aserción cuando Intento guardar la lista modificada dentro del detector de eventos. Esto solo ocurre cuando el elemento auditado ya tiene una (o más) instancia AuditLogEntry en la lista. Si no hay entradas, se crea una nueva lista y se agrega a la entidad que se está auditando, y esto está bien.
Creo que al aislar el problema de lo anterior, parecería estar (lazy) cargando la lista existente para agregar también la nueva instancia de AuditLogEntry. Sin embargo, no he podido avanzar más. Agregar 'Lazy = "False"' a la asignación de la lista no parece ayudar. Realmente estoy en los primeros días del uso de NHibernate, habiendo tomado prestados conceptos tanto del libro de cocina HN 3.0 como de este blog post. Mi código es muy similar a esto, pero intenta agregar el historial de auditoría al elemento que se está auditando en una lista (y como tal, creo que también debo hacer eso en el evento pre, en lugar de publicar la actualización).
una instantánea de las interfaces de entidades/clases en cuestión son:
public class AuditLogEntry : Entity
{
public virtual AuditEntryTypeEnum AuditEntryType { get; set; }
public virtual string EntityFullName { get; set; }
public virtual string EntityShortName { get; set; }
public virtual string Username { get; set; }
public virtual DateTime When { get; set; }
public virtual IList<AuditLogEntryDetail> Details { get; set; }
}
public interface IAuditTrackedEntity
{
Guid Id { get; }
IList<AuditLogEntry> ChangeHistory { get; set; }
}
public class AuditTrackedEntity : StampedEntity, IAuditTrackedEntity
{
public virtual IList<AuditLogEntry> ChangeHistory { get; set; }
}
public class LookupValue : AuditTrackedEntity
{
public virtual string Description { get; set; }
}
Para las asignaciones que tengo:
AuditTrackedEntry.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainObjects" namespace="DomainObjects.AuditTracking">
<class name="AuditLogEntry">
<id name="Id">
<generator class="guid.comb" />
</id>
<version name="Version" />
<property name="AuditEntryType"/>
<property name="EntityFullName"/>
<property name="EntityShortName"/>
<property name="Username"/>
<property name="When" column="`When`"/>
<list name ="Details" cascade="all">
<key column="AuditLogEntryId"/>
<list-index column="DetailsIndex" base="1"/>
<one-to-many class="AuditLogEntryDetail"/>
</list>
</class>
</hibernate-mapping>
lookupvalue.hbm .xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DomainObjects" namespace="DomainObjects">
<class name="LookupValue">
<id name="Id">
<generator class="guid.comb" />
</id>
<discriminator type="string">
<column name="LookupValueType" unique-key="UQ_TypeName" not-null="true" />
</discriminator>
<version name="Version" />
<property name="Description" unique-key="UQ_TypeName" not-null="true" />
<property name="CreatedBy" />
<property name="WhenCreated" />
<property name="ChangedBy" />
<property name="WhenChanged" />
<list name ="ChangeHistory">
<key column="EntityId"/>
<list-index column="ChangeIndex" base="1"/>
<one-to-many class="DomainObjects.AuditTracking.AuditLogEntry"/>
</list>
</class>
</hibernate-mapping>
El EventListener PreUpdate controlador de eventos llama al código siguiente: Las líneas que causan el problema se comentan cerca del final del bloque de código
public void TrackPreUpdate(IAuditTrackedEntity entity, object[] oldState, object[] state, IEntityPersister persister, IEventSource eventSource)
{
if (entity == null || entity is AuditLogEntry)
return;
var entityFullName = entity.GetType().FullName;
if (oldState == null)
{
throw new ArgumentNullException("No old state available for entity type '" + entityFullName +
"'. Make sure you're loading it into Session before modifying and saving it.");
}
var dirtyFieldIndexes = persister.FindDirty(state, oldState, entity, eventSource);
var session = eventSource.GetSession(EntityMode.Poco);
AuditLogEntry auditLogEntry = null;
foreach (var dirtyFieldIndex in dirtyFieldIndexes)
{
if (IsIngoredProperty(persister, dirtyFieldIndex))
continue;
var oldValue = GetStringValueFromStateArray(oldState, dirtyFieldIndex);
var newValue = GetStringValueFromStateArray(state, dirtyFieldIndex);
if (oldValue == newValue)
{
continue;
}
if (auditLogEntry == null)
{
auditLogEntry = new AuditLogEntry
{
AuditEntryType = AuditEntryTypeEnum.Update,
EntityShortName = entity.GetType().Name,
EntityFullName = entityFullName,
Username = Environment.UserName,
//EntityId = entity.Id,
When = DateTime.Now,
Details = new List<AuditLogEntryDetail>()
};
//**********************
// The next three lines cause a problem when included,
// collection [] was not processed by flush()
//**********************
if (entity.ChangeHistory == null)
entity.ChangeHistory = new List<AuditLogEntry>();
entity.ChangeHistory.Add(auditLogEntry);
session.Save(auditLogEntry);
}
var detail = new AuditLogEntryDetail
{
//AuditLogEntryId = auditLogEntry.Id,
PropertyName = persister.PropertyNames[dirtyFieldIndex],
OldValue = oldValue,
NewValue = newValue
};
session.Save(detail);
auditLogEntry.Details.Add(detail);
}
session.Flush();
}
Como se ha dicho anteriormente, en esta configuración me sale un error de aserción " colección [] no fue procesada por flush() ". Si elimino las tres líneas de arriba y la asignación de lista en lookupcode.hmb.xml, entonces todo funciona como se espera, aparte de que la entidad que se está auditando ya no contiene una referencia a sus propios elementos auditados.
Gracias por la actualización, es de esperar que tendrá la oportunidad de volver a examinar el código de mañana y probar su solución. Te dejaré saber lo que encuentro. –
Gracias por su ayuda, pero me temo que sigo teniendo problemas y cambiar el orden según lo recomendado no ha hecho ninguna diferencia. En diferentes configuraciones, puede obtener diferentes errores, pero comete errores de todos modos. Estoy de acuerdo en que parece ser la colección y me preguntaba si fue porque el código no estaba actualizando el nuevo estado para reflejar la adición de la colección a la entidad, sino que agrega que luego crea otro error ya sea de estado obsoleto o al actualizar una objeto en dos sesiones. –