Domanda

Sto cercando di scrivere un monitoraggio di audit per NHibernate che i ganci in caso PreUpdate. Ho una classe AuditLogEntry (quando, chi, ecc), che contiene un elenco di AuditLogEntryDetails (ovvero le singole proprietà che ha cambiato). Se mi isolo la classe AuditLogEntry dall'essere ente sottoposto poi le mie corse di codice senza errori. Tuttavia, se si aggiunge un elenco di AuditLogEntry di all'essere soggetto sottoposto a revisione allora il mio codice genera un

raccolta [DomainObjects.AuditTracking.AuditLogEntry.Details] non è stato elaborato da flush ()

errore di asserzione quando tento di salvare l'elenco modificato all'interno del listener di eventi. Questo accade solo quando l'oggetto sottoposto a revisione ha già uno (o più) istanza AuditLogEntry nella lista. Se non ci sono iscrizioni poi si crea un nuovo elenco e ha aggiunto all'entità sottoposta a revisione e questo va bene.

Credo che isolando la questione al di sopra di essa sembrerebbe essere in giro (pigro) di caricare l'elenco esistente per aggiungere la nuova istanza di AuditLogEntry troppo. Tuttavia sono stato in grado di progredire ulteriormente. L'aggiunta di 'pigro = 'false'' alla mappatura lista non sembra aiuto. Sono davvero nei primi giorni di utilizzo di NHibernate, avendo concetti presi in prestito sia dal HN 3.0 Cookbook e questo post sul blog . Il mio codice è molto simile a questo, ma i tentativi di aggiungere alla storia di revisione per l'essere oggetto oggetto di audit in una lista (e come tale penso che ho bisogno di fare, inoltre, che nel periodo pre, piuttosto che evento di aggiornamento post).

Un colpo a schiocco delle interfacce entità / classi in questione sono:

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; }
}

Per le mappature che ho:

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>

Il gestore di eventi EventListener PreUpdate chiama il codice seguente: Le linee che causano il problema sono commentate vicino alla fine del blocco di codice

    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();
    }

Come indicato in precedenza, in questa configurazione ricevo un errore di asserzione " raccolta [] non è stato elaborato da incasso () ". Se rimuovo le tre linee di cui sopra e la mappatura lista nella lookupcode.hmb.xml allora tutto funziona come previsto, oltre l'essere dell'ente sottoposto a revisione non contiene un riferimento ad esso è propri articoli sottoposti a revisione contabile.

È stato utile?

Soluzione

ci dava problema molto simile, esattamente stessa eccezione, ma in situazione diversa. Nessuna soluzione trovata ancora ...

Abbiamo NH listener di eventi l'implementazione del metodo IPreUpdateEventListener e OnPreUpdate utilizzato per registro di controllo. Tutto va bene per le proprietà semplice aggiornamento, la verifica sporca funziona bene, ma ci sono problemi con le collezioni pigri. Quando si aggiorna un oggetto che ha raccolta pigrizia e accedendo a un campo di oggetto nel metodo OnPreUpdate evento ascoltatore, la stessa eccezione come detto sopra viene generata. Quando lazy impostato su false, problema scompare.

Quindi, sembra che ci sia qualche problema con le collezioni pigri (e non esercita l'inizializzazione di raccolta prima di salvare). Il nostro problema non è collegato con la creazione di nuovi oggetti da collezione; solo la lettura oggetto esistente, solo il suo campo si accede dal listener di eventi causa il problema.

Quindi nel tuo caso, forse, lazy set false solo per l'associatioon potrebbe risolvere il problema, ma d'altra parte, probabilmente si vuole veramente avere la collezione di essere pigro. Così difficile da dire, se il problema ha una risoluzione o IInterceptor devono essere utilizzati al posto.

Altri suggerimenti

Ok, ho trovato il problema, questa linea è in realtà la causa del problema.

Details =  new List<AuditLogEntryDetail>()

Non è possibile inizializzare un insieme vuoto prima di salvare, perché l'EntityPersister non persisterà la raccolta, ma sarà errore che la raccolta non è stato elaborato.

Inoltre, una volta NHibernate chiama listener di eventi, cascate non funzionano (non so se questo è di progettazione o no). Quindi, anche se si sta aggiungendo l'elemento dettaglio per la raccolta più tardi, si sta solo chiamando salvare sul dettaglio, non il genitore, in modo che la modifica non viene propagata. Suggerirei ri-factoring in modo che gli elementi sono stati completati in questo ordine ...

Dettaglio, quindi salvare,

AuditLogEntry, quindi salvare,

entità, quindi l'aggiornamento.

Ho avuto esattamente lo stesso problema durante l'utilizzo di EventListener. Stavo scorrendo proprietà one-by-one per rilevare i cambiamenti, che comprendeva le collezioni enumerazione. Tuttavia quando ho aggiunto un assegno per la raccolta utilizzando NHibernateUtil.IsInitialized(collection), problema è scomparso. Non vorrei prendere-e-ignorare l'eccezione AssertionFailure dal momento che potrebbe avere effetti collaterali sconosciuti.

C'è una questione ancora aperta per risolvere questo problema. C'è una patch alla fine del tema che ha risolto a me.

https://nhibernate.jira.com/browse/NH-3226

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top