Domanda

Ho un ascoltatore di eventi (per i registri di controllo) che deve aggiungere le voci del registro di controllo all'associazione dell'oggetto:

public Company : IAuditable {
    // Other stuff removed for bravety
    IAuditLog IAuditable.CreateEntry() {
        var entry = new CompanyAudit();
        this.auditLogs.Add(entry);
        return entry;
    }
    public virtual IEnumerable<CompanyAudit> AuditLogs {
        get { return this.auditLogs }
    }
}

IL AuditLogs la raccolta è mappata con a cascata:

public class CompanyMap : ClassMap<Company> {
    public CompanyMap() {
        // Id and others removed fro bravety
        HasMany(x => x.AuditLogs).AsSet()
            .LazyLoad()
            .Access.ReadOnlyPropertyThroughCamelCaseField()
            .Cascade.All();
    }
}

E l'ascoltatore chiede semplicemente all'oggetto controllabile di creare voci di registro in modo che possa aggiornarle:

internal class AuditEventListener : IPreInsertEventListener, IPreUpdateEventListener {
    public bool OnPreUpdate(PreUpdateEvent ev) {
        var audit = ev.Entity as IAuditable;
        if (audit == null)
            return false;
        Log(audit);
        return false;
    }


    public bool OnPreInsert(PreInsertEvent ev) {
        var audit = ev.Entity as IAuditable;
        if (audit == null)
            return false;

        Log(audit);
        return false;
    }
    private static void Log(IAuditable auditable) {
        var entry = auditable.CreateAuditEntry();  // Doing this for every auditable property
        entry.CreatedAt = DateTime.Now;
        entry.Who = GetCurrentUser(); // Might potentially execute a query as it links current user with log entry
        // Also other information is set for entry here
    }
}

Il problema però è che lancia TransientObjectException quando si effettua la transazione:

NHibernate.TransientObjectException : object references an unsaved transient instance - save the transient instance before flushing. Type: CompanyAudit, Entity: CompanyAudit
    at NHibernate.Engine.ForeignKeys.GetEntityIdentifierIfNotUnsaved(String entityName, Object entity, ISessionImplementor session)
    at NHibernate.Type.EntityType.GetIdentifier(Object value, ISessionImplementor session)
    at NHibernate.Type.ManyToOneType.NullSafeSet(IDbCommand st, Object value, Int32 index, Boolean[] settable, ISessionImplementor session)
    at NHibernate.Persister.Collection.AbstractCollectionPersister.WriteElement(IDbCommand st, Object elt, Int32 i, ISessionImplementor session)
    at NHibernate.Persister.Collection.AbstractCollectionPersister.PerformInsert(Object ownerId, IPersistentCollection collection, IExpectation expectation, Object entry, Int32 index, Boolean useBatch, Boolean callable, ISessionImplementor session)
    at NHibernate.Persister.Collection.AbstractCollectionPersister.Recreate(IPersistentCollection collection, Object id, ISessionImplementor session)
    at NHibernate.Action.CollectionRecreateAction.Execute()
    at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
    at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
    at NHibernate.Engine.ActionQueue.ExecuteActions()
    at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
    at NHibernate.Event.Default.DefaultFlushEventListener.OnFlush(FlushEvent event)
    at NHibernate.Impl.SessionImpl.Flush()
    at NHibernate.Transaction.AdoTransaction.Commit()

Come il a cascata è impostato su Tutti Mi aspettavo che NH se ne occupasse.Ho anche provato a modificare la raccolta utilizzando state ma succede più o meno la stessa cosa.

Quindi la domanda è qual è l'ultima possibilità di modificare le associazioni dell'oggetto prima che venga salvato?

Grazie,
Dmitrij.

È stato utile?

Soluzione 2

Ho provato a utilizzare gli ascoltatori di eventi di NH con problemi diversi.
Quindi ho deciso di utilizzare Interceptor (basato su EmptyInterceptor).

Devo solo eseguire l'override SetSession, OnFlushDirty, OnSave metodi e collegarsi a quelli.

Consentono di creare oggetti adeguati e di renderli persistenti.

Quindi questa è la soluzione più praticabile finora.

Altri suggerimenti

http://ayende.com/Blog/archive/2009/04/29/nhibernate-ipreupdateeventlistener-amp-ipreinserteventlistener.aspx

In breve sembra che con l'attivazione di OnPreInsert, NHibernate abbia già determinato cosa deve essere aggiornato.Se in seguito qualcosa diventa sporco, è necessario aggiornare ulteriormente lo "stato dell'entità", la voce dell'oggetto nell'elenco degli oggetti "sporchi".

L'implementazione di IAuditable in Company apporta una modifica a tale oggetto;vale a dire, aggiungere un nuovo oggetto a una raccolta.Nel caso di creazione di oggetti completamente nuovi, la pratica migliore (come menzionato da Revin Hart nei commenti del post sul blog) sembra essere quella di creare una sessione figlia e salvare lì il nuovo oggetto.Sembra molto più semplice che aggiungere tutte le voci necessarie allo stato dell'entità con le chiamate Set().Prova a catturare IAuditLog dalla chiamata IAuditable.CreateEntry() e a salvarlo utilizzando un codice simile al seguente:

public bool OnPreInsert(PreInsertEvent ev) {
    var audit = ev.Entity as IAuditable;
    if (audit == null)
        return false;

    var log = Log(audit);

    ISession newSession = ev.Source.PersistenceContext.Session.GetSession();
    newSession.Save(log);
    return false;
}


private static IAuditLog Log(IAuditable auditable) {
    var entry = auditable.CreateAuditEntry();  // Doing this for every auditable property
    entry.CreatedAt = DateTime.Now;
    entry.Who = GetCurrentUser(); // Might potentially execute a query as it links current user with log entry

    return entry;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top